2
0

create.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package environment_groups
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. "github.com/porter-dev/porter/internal/kubernetes"
  7. "github.com/porter-dev/porter/internal/telemetry"
  8. v1 "k8s.io/api/core/v1"
  9. k8serror "k8s.io/apimachinery/pkg/api/errors"
  10. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  11. )
  12. // CreateOrUpdateBaseEnvironmentGroup creates a new environment group in the porter-env-group namespace. If porter-env-group does not exist, it will be created.
  13. // If no existing environmentGroup exists by this name, a new one will be created as version 1, denoted by the label "porter.run/environment-group-version: 1".
  14. // If an environmentGroup already exists by this name, a new version will be created, and the label will be updated to reflect the new version.
  15. // Providing the Version field to this function will be ignored in order to not accidentally overwrite versions
  16. func CreateOrUpdateBaseEnvironmentGroup(ctx context.Context, a *kubernetes.Agent, environmentGroup EnvironmentGroup, additionalLabels map[string]string) error {
  17. ctx, span := telemetry.NewSpan(ctx, "create-environment-group")
  18. defer span.End()
  19. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "environment-group", Value: environmentGroup.Name})
  20. if environmentGroup.Name == "" {
  21. return telemetry.Error(ctx, span, nil, "environment group name cannot be empty")
  22. }
  23. _, err := a.Clientset.CoreV1().Namespaces().Get(ctx, Namespace_EnvironmentGroups, metav1.GetOptions{})
  24. if err != nil {
  25. if !k8serror.IsNotFound(err) {
  26. return telemetry.Error(ctx, span, err, "unable to check if global environment group exists")
  27. }
  28. _, err = a.Clientset.CoreV1().Namespaces().Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: Namespace_EnvironmentGroups}}, metav1.CreateOptions{})
  29. if err != nil {
  30. return telemetry.Error(ctx, span, err, "unable to create global environment group")
  31. }
  32. }
  33. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "environment-group-namespace", Value: Namespace_EnvironmentGroups})
  34. latestEnvironmentGroup, err := latestBaseEnvironmentGroup(ctx, a, environmentGroup.Name)
  35. if err != nil {
  36. return telemetry.Error(ctx, span, err, "unable to get latest base environment group by name")
  37. }
  38. // If any of the secret variables are set to the dummy value (i.e. are unchanged), replace them with the existing value.
  39. for k, v := range environmentGroup.SecretVariables {
  40. if v == EnvGroupSecretDummyValue {
  41. existingValue, ok := latestEnvironmentGroup.SecretVariables[k]
  42. if !ok {
  43. return telemetry.Error(ctx, span, nil, "secret variable does not exist in latest environment group")
  44. }
  45. if string(existingValue) == "" {
  46. return telemetry.Error(ctx, span, nil, "secret variable value is empty")
  47. }
  48. environmentGroup.SecretVariables[k] = existingValue
  49. }
  50. }
  51. newEnvironmentGroup := EnvironmentGroup{
  52. Name: environmentGroup.Name,
  53. Variables: environmentGroup.Variables,
  54. SecretVariables: environmentGroup.SecretVariables,
  55. Version: latestEnvironmentGroup.Version + 1,
  56. CreatedAtUTC: environmentGroup.CreatedAtUTC,
  57. }
  58. err = createVersionedEnvironmentGroupInNamespace(ctx, a, newEnvironmentGroup, Namespace_EnvironmentGroups, additionalLabels)
  59. if err != nil {
  60. return telemetry.Error(ctx, span, err, "unable to create new versioned environment group")
  61. }
  62. return nil
  63. }
  64. // createEnvironmentGroupInTargetNamespace creates a new environment group in the target namespace. If you want to create a new base environment group, use CreateOrUpdateBaseEnvironmentGroup instead.
  65. // This should only be used for sync from a base environment to a target environment.
  66. // If the target namespace does not exist, it will be created for you.
  67. func createEnvironmentGroupInTargetNamespace(ctx context.Context, a *kubernetes.Agent, namespace string, environmentGroup EnvironmentGroup, additionalLabels map[string]string) (string, error) {
  68. ctx, span := telemetry.NewSpan(ctx, "create-environment-group-in-target")
  69. defer span.End()
  70. telemetry.WithAttributes(span,
  71. telemetry.AttributeKV{Key: "environment-group", Value: environmentGroup.Name},
  72. telemetry.AttributeKV{Key: "target-namespace", Value: namespace},
  73. )
  74. // var configMapName string
  75. configMapName := fmt.Sprintf("%s.%d", environmentGroup.Name, environmentGroup.Version)
  76. if environmentGroup.Name == "" {
  77. return configMapName, telemetry.Error(ctx, span, nil, "environment group name cannot be empty")
  78. }
  79. if environmentGroup.Version == 0 {
  80. return configMapName, telemetry.Error(ctx, span, nil, "environment group version cannot be empty")
  81. }
  82. if namespace == "" {
  83. return configMapName, telemetry.Error(ctx, span, nil, "target namespace cannot be empty")
  84. }
  85. _, err := a.Clientset.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
  86. if err != nil {
  87. if !k8serror.IsNotFound(err) {
  88. return configMapName, telemetry.Error(ctx, span, err, "unable to check if target namespace exists")
  89. }
  90. _, err = a.Clientset.CoreV1().Namespaces().Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}, metav1.CreateOptions{})
  91. if err != nil {
  92. return configMapName, telemetry.Error(ctx, span, err, "unable to create new target namespace")
  93. }
  94. }
  95. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "environment-group-namespace", Value: namespace})
  96. err = createVersionedEnvironmentGroupInNamespace(ctx, a, environmentGroup, namespace, additionalLabels)
  97. if err != nil {
  98. return configMapName, telemetry.Error(ctx, span, err, "error creating environment group clone in target namespace")
  99. }
  100. return configMapName, nil
  101. }
  102. // createVersionedEnvironmentGroupInNamespace creates a new environment group in the target namespace. This is used to keep the configmap and secret version for an environment variable in sync
  103. func createVersionedEnvironmentGroupInNamespace(ctx context.Context, a *kubernetes.Agent, environmentGroup EnvironmentGroup, targetNamespace string, additionalLabels map[string]string) error {
  104. ctx, span := telemetry.NewSpan(ctx, "create-environment-group-on-cluster")
  105. defer span.End()
  106. configMap := v1.ConfigMap{
  107. ObjectMeta: metav1.ObjectMeta{
  108. Name: fmt.Sprintf("%s.%d", environmentGroup.Name, environmentGroup.Version),
  109. Namespace: targetNamespace,
  110. Labels: map[string]string{
  111. LabelKey_EnvironmentGroupName: environmentGroup.Name,
  112. LabelKey_EnvironmentGroupVersion: strconv.Itoa(environmentGroup.Version),
  113. },
  114. },
  115. Data: environmentGroup.Variables,
  116. }
  117. for k, v := range additionalLabels {
  118. configMap.Labels[k] = v
  119. }
  120. err := createConfigMapWithVersion(ctx, a, configMap, environmentGroup.Version)
  121. if err != nil {
  122. return telemetry.Error(ctx, span, err, "unable to create new environment group variables version")
  123. }
  124. secretData := make(map[string][]byte)
  125. for k, v := range environmentGroup.SecretVariables {
  126. secretData[k] = []byte(v)
  127. }
  128. secret := v1.Secret{
  129. ObjectMeta: metav1.ObjectMeta{
  130. Name: fmt.Sprintf("%s.%d", environmentGroup.Name, environmentGroup.Version),
  131. Namespace: targetNamespace,
  132. Labels: map[string]string{
  133. LabelKey_EnvironmentGroupName: environmentGroup.Name,
  134. LabelKey_EnvironmentGroupVersion: strconv.Itoa(environmentGroup.Version),
  135. },
  136. },
  137. Data: secretData,
  138. }
  139. for k, v := range additionalLabels {
  140. secret.Labels[k] = v
  141. }
  142. err = createSecretWithVersion(ctx, a, secret, environmentGroup.Version)
  143. if err != nil {
  144. return telemetry.Error(ctx, span, err, "unable to create new environment group secret variables version")
  145. }
  146. return nil
  147. }
  148. func createConfigMapWithVersion(ctx context.Context, a *kubernetes.Agent, configMap v1.ConfigMap, version int) error {
  149. ctx, span := telemetry.NewSpan(ctx, "create-environment-group-configmap")
  150. defer span.End()
  151. telemetry.WithAttributes(span,
  152. telemetry.AttributeKV{Key: "configmap-label", Value: configMap.Labels[LabelKey_EnvironmentGroupName]},
  153. telemetry.AttributeKV{Key: "configmap-version", Value: configMap.Labels[LabelKey_EnvironmentGroupVersion]},
  154. telemetry.AttributeKV{Key: "configmap-name", Value: configMap.Name},
  155. telemetry.AttributeKV{Key: "configmap-namespace", Value: configMap.Namespace},
  156. )
  157. _, err := a.Clientset.CoreV1().ConfigMaps(configMap.Namespace).Create(ctx, &configMap, metav1.CreateOptions{})
  158. if err != nil {
  159. return telemetry.Error(ctx, span, err, "unable to create environment group configmap")
  160. }
  161. return nil
  162. }
  163. func createSecretWithVersion(ctx context.Context, a *kubernetes.Agent, secret v1.Secret, version int) error {
  164. ctx, span := telemetry.NewSpan(ctx, "create-environment-group-secret")
  165. defer span.End()
  166. telemetry.WithAttributes(span,
  167. telemetry.AttributeKV{Key: "secret-label", Value: secret.Labels[LabelKey_EnvironmentGroupName]},
  168. telemetry.AttributeKV{Key: "secret-version", Value: secret.Labels[LabelKey_EnvironmentGroupVersion]},
  169. telemetry.AttributeKV{Key: "secret-name", Value: secret.Name},
  170. telemetry.AttributeKV{Key: "secret-namespace", Value: secret.Namespace},
  171. )
  172. _, err := a.Clientset.CoreV1().Secrets(secret.Namespace).Create(ctx, &secret, metav1.CreateOptions{})
  173. if err != nil {
  174. return telemetry.Error(ctx, span, err, "unable to create environment group secret")
  175. }
  176. return nil
  177. }