create.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package envgroup
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "github.com/porter-dev/porter/api/types"
  9. "github.com/porter-dev/porter/internal/helm"
  10. "github.com/porter-dev/porter/internal/kubernetes"
  11. "github.com/stefanmcshane/helm/pkg/release"
  12. v1 "k8s.io/api/core/v1"
  13. )
  14. func ConvertV1ToV2EnvGroup(agent *kubernetes.Agent, name, namespace string) (*v1.ConfigMap, error) {
  15. cm, err := agent.GetConfigMap(name, namespace)
  16. if err != nil {
  17. return nil, err
  18. }
  19. secret, err := agent.GetSecret(name, namespace)
  20. if err != nil {
  21. return nil, err
  22. }
  23. variables := make(map[string]string)
  24. secretVariables := make(map[string]string)
  25. for key, val := range cm.Data {
  26. if strings.Contains(val, "PORTERSECRET") {
  27. secretVariables[key] = val
  28. } else {
  29. variables[key] = val
  30. }
  31. }
  32. for key, val := range secret.Data {
  33. secretVariables[key] = string(val)
  34. }
  35. envGroup, err := CreateEnvGroup(agent, types.ConfigMapInput{
  36. Name: name,
  37. Namespace: namespace,
  38. Variables: variables,
  39. SecretVariables: secretVariables,
  40. })
  41. if err != nil {
  42. return nil, err
  43. }
  44. // delete the old configmap
  45. // note: we keep the old secret to ensure existing secret references are kept intact
  46. if err := agent.DeleteConfigMap(name, namespace); err != nil {
  47. return nil, err
  48. }
  49. return envGroup, nil
  50. }
  51. func CreateEnvGroup(agent *kubernetes.Agent, input types.ConfigMapInput) (*v1.ConfigMap, error) {
  52. // look for a latest configmap
  53. oldCM, latestVersion, err := agent.GetLatestVersionedConfigMap(input.Name, input.Namespace)
  54. if err != nil && !errors.Is(err, kubernetes.IsNotFoundError) {
  55. return nil, err
  56. } else if err != nil {
  57. latestVersion = 1
  58. } else {
  59. latestVersion += 1
  60. }
  61. apps := make([]string, 0)
  62. if oldCM != nil {
  63. oldEG, err := ToEnvGroup(oldCM)
  64. if err == nil {
  65. apps = oldEG.Applications
  66. }
  67. }
  68. oldSecret, _, err := agent.GetLatestVersionedSecret(input.Name, input.Namespace)
  69. if input.SecretVariables == nil {
  70. input.SecretVariables = make(map[string]string)
  71. }
  72. if err != nil && !errors.Is(err, kubernetes.IsNotFoundError) {
  73. return nil, err
  74. } else if err == nil && oldSecret != nil {
  75. // In this case, we find all old variables referencing a secret value, and add those
  76. // values to the new secret variables. The frontend will only send **new** secret values.
  77. for key1, val1 := range input.Variables {
  78. if strings.Contains(val1, "PORTERSECRET") {
  79. // get that value from the secret
  80. for key2, val2 := range oldSecret.Data {
  81. if key2 == key1 {
  82. input.SecretVariables[key1] = string(val2)
  83. }
  84. }
  85. }
  86. }
  87. }
  88. // add all secret env variables to configmap with value PORTERSECRET_${configmap_name}
  89. for key := range input.SecretVariables {
  90. input.Variables[key] = fmt.Sprintf("PORTERSECRET_%s.v%d", input.Name, latestVersion)
  91. }
  92. cm, err := agent.CreateVersionedConfigMap(input.Name, input.Namespace, latestVersion, input.Variables, apps...)
  93. if err != nil {
  94. return nil, err
  95. }
  96. secretData := EncodeSecrets(input.SecretVariables)
  97. // create secret first
  98. if _, err := agent.CreateLinkedVersionedSecret(input.Name, input.Namespace, cm.ObjectMeta.Name, latestVersion, secretData); err != nil {
  99. return nil, err
  100. }
  101. return cm, err
  102. }
  103. func ToEnvGroup(configMap *v1.ConfigMap) (*types.EnvGroup, error) {
  104. res := &types.EnvGroup{
  105. CreatedAt: configMap.ObjectMeta.CreationTimestamp.Time,
  106. Namespace: configMap.Namespace,
  107. Variables: configMap.Data,
  108. }
  109. // if the label "porter"="true" exists, this is a V1 env group
  110. porterLabel, porterLabelExists := configMap.Labels["porter"]
  111. if porterLabelExists && porterLabel == "true" {
  112. res.MetaVersion = 1
  113. res.Name = configMap.ObjectMeta.Name
  114. return res, nil
  115. }
  116. // set the meta version to 2 if porter label is not captured
  117. res.MetaVersion = 2
  118. // get the name
  119. name, nameExists := configMap.Labels["envgroup"]
  120. if !nameExists {
  121. return nil, fmt.Errorf("not a valid configmap: envgroup label does not exist")
  122. }
  123. res.Name = name
  124. // get the version
  125. versionLabelStr, versionLabelExists := configMap.Labels["version"]
  126. if !versionLabelExists {
  127. return nil, fmt.Errorf("not a valid configmap: version label does not exist")
  128. }
  129. versionInt, err := strconv.Atoi(versionLabelStr)
  130. if err != nil {
  131. return nil, fmt.Errorf("not a valid configmap, error converting version: %v", err)
  132. }
  133. res.Version = uint(versionInt)
  134. // get applications, if they exist
  135. appStr, appAnnonExists := configMap.Annotations[kubernetes.PorterAppAnnotationName]
  136. if appAnnonExists && appStr != "" {
  137. res.Applications = strings.Split(appStr, ",")
  138. } else {
  139. res.Applications = []string{}
  140. }
  141. return res, nil
  142. }
  143. func GetSyncedReleases(helmAgent *helm.Agent, configMap *v1.ConfigMap) ([]*release.Release, error) {
  144. res := make([]*release.Release, 0)
  145. // get applications, if they exist
  146. appStr, appAnnonExists := configMap.Annotations[kubernetes.PorterAppAnnotationName]
  147. if !appAnnonExists || appStr == "" {
  148. return res, nil
  149. }
  150. appStrArr := strings.Split(appStr, ",")
  151. // list all latest helm releases and check them against app string
  152. releases, err := helmAgent.ListReleases(context.Background(), configMap.Namespace, &types.ReleaseListFilter{
  153. StatusFilter: []string{
  154. "deployed",
  155. "uninstalled",
  156. "pending",
  157. "pending-install",
  158. "pending-upgrade",
  159. "pending-rollback",
  160. "failed",
  161. },
  162. })
  163. if err != nil {
  164. return nil, err
  165. }
  166. for _, rel := range releases {
  167. for _, appName := range appStrArr {
  168. if rel.Name == appName {
  169. res = append(res, rel)
  170. }
  171. }
  172. }
  173. return res, nil
  174. }
  175. func EncodeSecrets(data map[string]string) map[string][]byte {
  176. res := make(map[string][]byte)
  177. for key, rawValue := range data {
  178. res[key] = []byte(rawValue)
  179. }
  180. return res
  181. }