diff --git a/go.mod b/go.mod index ee55cdef..ee72880d 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/orcaman/concurrent-map/v2 v2.0.1 github.com/osteele/liquid v1.3.2 github.com/pkg/errors v0.9.1 - github.com/pluralsh/console-client-go v0.1.11 + github.com/pluralsh/console-client-go v0.1.12 github.com/pluralsh/controller-reconcile-helper v0.0.4 github.com/pluralsh/gophoenix v0.1.3-0.20231201014135-dff1b4309e34 github.com/pluralsh/polly v0.1.7 diff --git a/go.sum b/go.sum index 42265d75..aee9538b 100644 --- a/go.sum +++ b/go.sum @@ -512,8 +512,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pluralsh/console-client-go v0.1.11 h1:QqbLOtEBQtfj/7gg7mDCwYLX3F7wCAiol9T5zCVAKq4= -github.com/pluralsh/console-client-go v0.1.11/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= +github.com/pluralsh/console-client-go v0.1.12 h1:0iZukdLbakbxR6Wz0ykEk1UN13oSEzV8egDhjauNris= +github.com/pluralsh/console-client-go v0.1.12/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo= github.com/pluralsh/controller-reconcile-helper v0.0.4 h1:1o+7qYSyoeqKFjx+WgQTxDz4Q2VMpzprJIIKShxqG0E= github.com/pluralsh/controller-reconcile-helper v0.0.4/go.mod h1:AfY0gtteD6veBjmB6jiRx/aR4yevEf6K0M13/pGan/s= github.com/pluralsh/gophoenix v0.1.3-0.20231201014135-dff1b4309e34 h1:ab2PN+6if/Aq3/sJM0AVdy1SYuMAnq4g20VaKhTm/Bw= diff --git a/internal/utils/kubernetes.go b/internal/utils/kubernetes.go index c6ac8e03..1108d107 100644 --- a/internal/utils/kubernetes.go +++ b/internal/utils/kubernetes.go @@ -3,6 +3,7 @@ package utils import ( "context" "fmt" + "maps" "os" "reflect" "strings" @@ -145,28 +146,38 @@ func GetOperatorNamespace() (string, error) { return string(ns), nil } -func CheckNamespace(clientset kubernetes.Clientset, namespace string) error { +func CheckNamespace(clientset kubernetes.Clientset, namespace string, labels, annotations map[string]string) error { if namespace == "" { return nil } ctx := context.Background() nsClient := clientset.CoreV1().Namespaces() - _, err := nsClient.Get(ctx, namespace, metav1.GetOptions{}) - if err == nil { - return nil + existing, err := nsClient.Get(ctx, namespace, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + if _, err = nsClient.Create(ctx, &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + Annotations: annotations, + Labels: labels, + }, + }, metav1.CreateOptions{}); err != nil { + return err + } + return nil + } + return err } - _, err = nsClient.Create(ctx, &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespace, - }, - }, metav1.CreateOptions{}) - - if err != nil { - if !apierrors.IsAlreadyExists(err) { + // update labels and annotations + if (!reflect.DeepEqual(labels, existing.Labels) && labels != nil) || (!reflect.DeepEqual(annotations, existing.Annotations) && annotations != nil) { + maps.Copy(existing.Labels, labels) + maps.Copy(existing.Annotations, annotations) + if _, err := nsClient.Update(ctx, existing, metav1.UpdateOptions{}); err != nil { return err } } + return nil } diff --git a/internal/utils/maps.go b/internal/utils/maps.go new file mode 100644 index 00000000..6fe2402d --- /dev/null +++ b/internal/utils/maps.go @@ -0,0 +1,12 @@ +package utils + +func ConvertMap(in map[string]interface{}) map[string]string { + res := make(map[string]string) + for k, v := range in { + value, ok := v.(string) + if ok { + res[k] = value + } + } + return res +} diff --git a/pkg/controller/namespaces/reconciler.go b/pkg/controller/namespaces/reconciler.go index a97697c0..67a7381d 100644 --- a/pkg/controller/namespaces/reconciler.go +++ b/pkg/controller/namespaces/reconciler.go @@ -3,11 +3,13 @@ package namespaces import ( "context" "fmt" + "maps" "reflect" "time" console "github.com/pluralsh/console-client-go" clienterrors "github.com/pluralsh/deployment-operator/internal/errors" + "github.com/pluralsh/deployment-operator/internal/utils" "github.com/pluralsh/deployment-operator/pkg/client" "github.com/pluralsh/deployment-operator/pkg/controller" "github.com/pluralsh/deployment-operator/pkg/websocket" @@ -116,49 +118,51 @@ func (n *NamespaceReconciler) Reconcile(ctx context.Context, id string) (reconci func (n *NamespaceReconciler) UpsertNamespace(ctx context.Context, fragment *console.ManagedNamespaceFragment) error { var labels map[string]string var annotations map[string]string + createNamespace := true if fragment.Labels != nil { - labels = convertMap(fragment.Labels) + labels = utils.ConvertMap(fragment.Labels) } if fragment.Annotations != nil { - annotations = convertMap(fragment.Annotations) + annotations = utils.ConvertMap(fragment.Annotations) } - existing := &v1.Namespace{} - err := n.K8sClient.Get(ctx, ctrlclient.ObjectKey{Name: fragment.Name}, existing) - if err != nil { - if apierrors.IsNotFound(err) { - if err := n.K8sClient.Create(ctx, &v1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: fragment.Name, - Labels: labels, - Annotations: annotations, - }, - }); err != nil { - return err - } - return nil + if fragment.Service != nil && fragment.Service.SyncConfig != nil { + if fragment.Service.SyncConfig.NamespaceMetadata != nil { + maps.Copy(labels, utils.ConvertMap(fragment.Service.SyncConfig.NamespaceMetadata.Labels)) + maps.Copy(annotations, utils.ConvertMap(fragment.Service.SyncConfig.NamespaceMetadata.Annotations)) + } + if fragment.Service.SyncConfig.CreateNamespace != nil { + createNamespace = *fragment.Service.SyncConfig.CreateNamespace } - return err } - if !reflect.DeepEqual(labels, existing.Labels) || !reflect.DeepEqual(annotations, existing.Annotations) { - existing.Labels = labels - existing.Annotations = annotations - if err := n.K8sClient.Update(ctx, existing); err != nil { + if createNamespace { + existing := &v1.Namespace{} + err := n.K8sClient.Get(ctx, ctrlclient.ObjectKey{Name: fragment.Name}, existing) + if err != nil { + if apierrors.IsNotFound(err) { + if err := n.K8sClient.Create(ctx, &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: fragment.Name, + Labels: labels, + Annotations: annotations, + }, + }); err != nil { + return err + } + return nil + } return err } - } - return nil -} - -func convertMap(in map[string]interface{}) map[string]string { - res := make(map[string]string) - for k, v := range in { - value, ok := v.(string) - if ok { - res[k] = value + // update labels and annotations + if !reflect.DeepEqual(labels, existing.Labels) || !reflect.DeepEqual(annotations, existing.Annotations) { + existing.Labels = labels + existing.Annotations = annotations + if err := n.K8sClient.Update(ctx, existing); err != nil { + return err + } } } - return res + return nil } diff --git a/pkg/controller/service/reconciler.go b/pkg/controller/service/reconciler.go index a25b10d9..8e9cb532 100644 --- a/pkg/controller/service/reconciler.go +++ b/pkg/controller/service/reconciler.go @@ -296,7 +296,8 @@ func (s *ServiceReconciler) Reconcile(ctx context.Context, id string) (result re vcache := manis.VersionCache(manifests) logger.Info("Apply service", "name", svc.Name, "namespace", svc.Namespace) - if err = s.CheckNamespace(svc.Namespace); err != nil { + + if err = s.CheckNamespace(svc.Namespace, svc.SyncConfig); err != nil { logger.Error(err, "failed to check namespace") return } @@ -331,8 +332,24 @@ func (s *ServiceReconciler) Reconcile(ctx context.Context, id string) (result re return } -func (s *ServiceReconciler) CheckNamespace(namespace string) error { - return utils.CheckNamespace(*s.Clientset, namespace) +func (s *ServiceReconciler) CheckNamespace(namespace string, syncConfig *console.GetServiceDeploymentForAgent_ServiceDeployment_SyncConfig) error { + createNamespace := true + var labels map[string]string + var annotations map[string]string + + if syncConfig != nil { + if syncConfig.NamespaceMetadata != nil { + labels = utils.ConvertMap(syncConfig.NamespaceMetadata.Labels) + annotations = utils.ConvertMap(syncConfig.NamespaceMetadata.Annotations) + } + if syncConfig.CreateNamespace != nil { + createNamespace = *syncConfig.CreateNamespace + } + } + if createNamespace { + return utils.CheckNamespace(*s.Clientset, namespace, labels, annotations) + } + return nil } func (s *ServiceReconciler) SplitObjects(id string, objs []*unstructured.Unstructured) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {