diff --git a/hack/local-run-test/Makefile b/hack/local-run-test/Makefile index 3be8a7e5..2e222d03 100644 --- a/hack/local-run-test/Makefile +++ b/hack/local-run-test/Makefile @@ -363,7 +363,7 @@ run-manager-local: go --metrics-bind-address :8085 \ --cert-dir . \ --zap-log-level=$(LOGLEVEL) \ - --zap-devel=false \ + --zap-devel=true \ --workspace-urlbase-protocol=https \ --workspace-urlbase-host=$(DEFAULT_URLBASE_HOST) \ --workspace-urlbase-domain=$(DOMAIN) diff --git a/internal/controllers/__snapshots__/user_controller_test.snap b/internal/controllers/__snapshots__/user_controller_test.snap index 3746ab7d..c89c6a30 100644 --- a/internal/controllers/__snapshots__/user_controller_test.snap +++ b/internal/controllers/__snapshots__/user_controller_test.snap @@ -34,14 +34,14 @@ SnapShot = """ }, \"addons\": [ { - \"kind\": \"Instance\", - \"namespace\": \"cosmo-user-ua\", - \"name\": \"useraddon-namespaced-addon\", + \"kind\": \"ClusterInstance\", + \"name\": \"useraddon-ua-cluster-addon\", \"apiVersion\": \"cosmo-workspace.github.io/v1alpha1\" }, { - \"kind\": \"ClusterInstance\", - \"name\": \"useraddon-ua-cluster-addon\", + \"kind\": \"Instance\", + \"namespace\": \"cosmo-user-ua\", + \"name\": \"useraddon-namespaced-addon\", \"apiVersion\": \"cosmo-workspace.github.io/v1alpha1\" } ] @@ -84,14 +84,14 @@ SnapShot = """ }, \"addons\": [ { - \"kind\": \"Instance\", - \"namespace\": \"cosmo-user-ua\", - \"name\": \"useraddon-namespaced-addon\", + \"kind\": \"ClusterInstance\", + \"name\": \"useraddon-ua-cluster-addon\", \"apiVersion\": \"cosmo-workspace.github.io/v1alpha1\" }, { - \"kind\": \"ClusterInstance\", - \"name\": \"useraddon-ua-cluster-addon\", + \"kind\": \"Instance\", + \"namespace\": \"cosmo-user-ua\", + \"name\": \"useraddon-namespaced-addon\", \"apiVersion\": \"cosmo-workspace.github.io/v1alpha1\" } ] diff --git a/internal/controllers/__snapshots__/workspace_controller_test.snap b/internal/controllers/__snapshots__/workspace_controller_test.snap index 911cb32c..a8b625fd 100644 --- a/internal/controllers/__snapshots__/workspace_controller_test.snap +++ b/internal/controllers/__snapshots__/workspace_controller_test.snap @@ -5,6 +5,9 @@ SnapShot = """ \"name\": \"ws-test\", \"namespace\": \"cosmo-user-wsctltest\", \"creationTimestamp\": null, + \"labels\": { + \"cosmo-workspace.github.io/type\": \"workspace\" + }, \"ownerReferences\": [ { \"apiVersion\": \"cosmo-workspace.github.io/v1alpha1\", @@ -33,7 +36,7 @@ SnapShot = """ { \"target\": { \"kind\": \"Service\", - \"name\": \"ws-svc\", + \"name\": \"ws-test-ws-svc\", \"apiVersion\": \"v1\" }, \"patch\": \"[{\\\"op\\\": \\\"replace\\\",\\\"path\\\": \\\"/spec/ports\\\",\\\"value\\\": []}]\" @@ -41,7 +44,7 @@ SnapShot = """ { \"target\": { \"kind\": \"Deployment\", - \"name\": \"ws-dep\", + \"name\": \"ws-test-ws-dep\", \"apiVersion\": \"apps/v1\" }, \"patch\": \"[{\\\"op\\\": \\\"replace\\\",\\\"path\\\": \\\"/spec/replicas\\\",\\\"value\\\": 1}]\" @@ -91,6 +94,9 @@ SnapShot = """ \"name\": \"ws-test\", \"namespace\": \"cosmo-user-wsctltest\", \"creationTimestamp\": null, + \"labels\": { + \"cosmo-workspace.github.io/type\": \"workspace\" + }, \"ownerReferences\": [ { \"apiVersion\": \"cosmo-workspace.github.io/v1alpha1\", @@ -119,7 +125,7 @@ SnapShot = """ { \"target\": { \"kind\": \"Service\", - \"name\": \"ws-svc\", + \"name\": \"ws-test-ws-svc\", \"apiVersion\": \"v1\" }, \"patch\": \"[{\\\"op\\\": \\\"replace\\\",\\\"path\\\": \\\"/spec/ports\\\",\\\"value\\\": [{\\\"name\\\":\\\"port3000\\\",\\\"protocol\\\":\\\"TCP\\\",\\\"port\\\":3000,\\\"targetPort\\\":30000}]}]\" @@ -127,7 +133,7 @@ SnapShot = """ { \"target\": { \"kind\": \"Deployment\", - \"name\": \"ws-dep\", + \"name\": \"ws-test-ws-dep\", \"apiVersion\": \"apps/v1\" }, \"patch\": \"[{\\\"op\\\": \\\"replace\\\",\\\"path\\\": \\\"/spec/replicas\\\",\\\"value\\\": 0}]\" diff --git a/internal/controllers/cluster_instance_controller.go b/internal/controllers/cluster_instance_controller.go index c70ace67..c3630280 100644 --- a/internal/controllers/cluster_instance_controller.go +++ b/internal/controllers/cluster_instance_controller.go @@ -81,7 +81,7 @@ func (r *ClusterInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Requ log.Error(err, "failed to update InstanceStatus") return ctrl.Result{}, err } - log.Debug().Info("status updated") + log.Info("status updated") } log.Debug().Info("finish reconcile") diff --git a/internal/controllers/instance_controller.go b/internal/controllers/instance_controller.go index 390e3923..c5cbce58 100644 --- a/internal/controllers/instance_controller.go +++ b/internal/controllers/instance_controller.go @@ -3,6 +3,7 @@ package controllers import ( "context" "fmt" + "sort" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -90,7 +91,7 @@ func (r *InstanceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c log.Error(err, "failed to update InstanceStatus") return ctrl.Result{}, err } - log.Debug().Info("status updated") + log.Info("status updated") } log.Debug().Info("finish reconcile") @@ -197,7 +198,7 @@ func (r *instanceReconciler) reconcileObjects(ctx context.Context, inst cosmov1a // garbage collection if len(errs) == 0 && !cosmov1alpha1.IsPruneDisabled(inst) { - log.Info("start garbage collection") + log.Debug().Info("checking garbage collection") shouldDeletes := objectRefNotExistsInMap(lastApplied, currAppliedMap) for _, d := range shouldDeletes { if skip, err := prune(ctx, r.Client, d); err != nil { @@ -247,12 +248,25 @@ func sliceToObjectMap(s []cosmov1alpha1.ObjectRef) map[types.UID]cosmov1alpha1.O } func objectRefMapToSlice(m map[types.UID]cosmov1alpha1.ObjectRef) []cosmov1alpha1.ObjectRef { + if len(m) == 0 { + return nil + } s := make([]cosmov1alpha1.ObjectRef, len(m)) var i int for _, v := range m { s[i] = v i++ } + sort.SliceStable(s, func(i, j int) bool { + x, y := s[i], s[j] + if x.APIVersion != y.APIVersion { + return x.APIVersion < y.APIVersion + } else if x.Kind != y.Kind { + return x.Kind < y.Kind + } else { + return x.Name < y.Name + } + }) return s } diff --git a/internal/controllers/user_controller.go b/internal/controllers/user_controller.go index dd9fbeee..a5f4b575 100644 --- a/internal/controllers/user_controller.go +++ b/internal/controllers/user_controller.go @@ -19,7 +19,7 @@ import ( cosmov1alpha1 "github.com/cosmo-workspace/cosmo/api/v1alpha1" "github.com/cosmo-workspace/cosmo/pkg/auth/password" "github.com/cosmo-workspace/cosmo/pkg/clog" - "github.com/cosmo-workspace/cosmo/pkg/kubeutil" + "github.com/cosmo-workspace/cosmo/pkg/instance" "github.com/cosmo-workspace/cosmo/pkg/useraddon" ) @@ -68,11 +68,10 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. user.Status.Namespace = cosmov1alpha1.ObjectRef{ ObjectReference: corev1.ObjectReference{ - APIVersion: gvk.GroupVersion().String(), - Kind: gvk.Kind, - Name: ns.GetName(), - UID: ns.GetUID(), - ResourceVersion: ns.GetResourceVersion(), + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: ns.GetName(), + UID: ns.GetUID(), }, CreationTimestamp: &ns.CreationTimestamp, } @@ -105,9 +104,18 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. log.Error(errors.New("instance is nil"), "addon has no Template or ClusterTemplate", "addon", addon) continue } + tmpl := useraddon.EmptyTemplateObject(addon) + if err := r.Get(ctx, types.NamespacedName{Name: tmpl.GetName()}, tmpl); err != nil { + addonErrs = append(addonErrs, fmt.Errorf("failed to create or update addon %s: failed to fetch template: %w", tmpl.GetName(), err)) + continue + } - op, err := kubeutil.CreateOrUpdate(ctx, r.Client, inst, func() error { - return useraddon.PatchUserAddonInstanceAsDesired(inst, addon, user, r.Scheme) + op, err := controllerutil.CreateOrUpdate(ctx, r.Client, inst, func() error { + if err := useraddon.PatchUserAddonInstanceAsDesired(inst, addon, user, r.Scheme); err != nil { + return err + } + instance.Mutate(inst, tmpl) + return nil }) if err != nil { addonErrs = append(addonErrs, fmt.Errorf("failed to create or update addon %s :%w", inst.GetSpec().Template.Name, err)) @@ -117,6 +125,8 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. if op != controllerutil.OperationResultNone { log.Info("addon synced", "addon", addon) r.Recorder.Eventf(&user, corev1.EventTypeNormal, "Addon Synced", fmt.Sprintf("addon %s is %s", addon.Template.Name, op)) + } else { + log.Debug().Info("the result of update addon instance operation is None", "addon", addon) } ct := inst.GetCreationTimestamp() @@ -127,12 +137,11 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. } currAddonsMap[inst.GetUID()] = cosmov1alpha1.ObjectRef{ ObjectReference: corev1.ObjectReference{ - APIVersion: gvk.GroupVersion().String(), - Kind: gvk.Kind, - Name: inst.GetName(), - Namespace: inst.GetNamespace(), - UID: inst.GetUID(), - ResourceVersion: inst.GetResourceVersion(), + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: inst.GetName(), + Namespace: inst.GetNamespace(), + UID: inst.GetUID(), }, CreationTimestamp: &ct, } @@ -145,19 +154,20 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. log.Error(e, "failed to create or update user addon") } user.Status.Phase = "AddonFailed" + err = addonErrs[0] } // update user status - if !equality.Semantic.DeepEqual(currentUser, user) { + if !equality.Semantic.DeepEqual(currentUser, &user) { log.Debug().PrintObjectDiff(currentUser, &user) if err := r.Status().Update(ctx, &user); err != nil { return ctrl.Result{}, err } - log.Debug().Info("status updated") + log.Info("status updated") } if user.Status.Phase != "AddonFailed" && !cosmov1alpha1.IsPruneDisabled(&user) { - log.Info("start garbage collection") + log.Debug().Info("checking garbage collection") shouldDeletes := objectRefNotExistsInMap(lastAddons, currAddonsMap) for _, d := range shouldDeletes { if skip, err := prune(ctx, r.Client, d); err != nil { diff --git a/internal/controllers/user_controller_test.go b/internal/controllers/user_controller_test.go index 8689c1b2..d621e38c 100644 --- a/internal/controllers/user_controller_test.go +++ b/internal/controllers/user_controller_test.go @@ -208,7 +208,6 @@ spec: Expect(user.Status.Namespace.Name).Should(BeEquivalentTo(createdNs.GetName())) Expect(user.Status.Namespace.UID).Should(BeEquivalentTo(createdNs.GetUID())) - Expect(user.Status.Namespace.ResourceVersion).Should(BeEquivalentTo(createdNs.GetResourceVersion())) By("check password secret is created") @@ -323,7 +322,6 @@ spec: Expect(user.Status.Namespace.Name).Should(BeEquivalentTo(createdNs.GetName())) Expect(user.Status.Namespace.UID).Should(BeEquivalentTo(createdNs.GetUID())) - Expect(user.Status.Namespace.ResourceVersion).Should(BeEquivalentTo(createdNs.GetResourceVersion())) By("check password secret is not created") diff --git a/internal/controllers/workspace_controller.go b/internal/controllers/workspace_controller.go index c6e828fd..1d11dd86 100644 --- a/internal/controllers/workspace_controller.go +++ b/internal/controllers/workspace_controller.go @@ -18,7 +18,7 @@ import ( cosmov1alpha1 "github.com/cosmo-workspace/cosmo/api/v1alpha1" "github.com/cosmo-workspace/cosmo/pkg/clog" - "github.com/cosmo-workspace/cosmo/pkg/kubeutil" + "github.com/cosmo-workspace/cosmo/pkg/instance" "github.com/cosmo-workspace/cosmo/pkg/workspace" ) @@ -60,12 +60,22 @@ func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } ws.Status.Config = cfg - // sync instance inst := &cosmov1alpha1.Instance{} inst.SetName(ws.Name) inst.SetNamespace(ws.Namespace) - op, err := kubeutil.CreateOrUpdate(ctx, r.Client, inst, func() error { - return workspace.PatchWorkspaceInstanceAsDesired(inst, ws, r.Scheme) + + tmpl := &cosmov1alpha1.Template{} + if err := r.Get(ctx, types.NamespacedName{Name: ws.Spec.Template.Name}, tmpl); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to fetch template %s: %w", ws.Spec.Template.Name, err) + } + + // sync + op, err := controllerutil.CreateOrUpdate(ctx, r.Client, inst, func() error { + if err := workspace.PatchWorkspaceInstanceAsDesired(inst, ws, r.Scheme); err != nil { + return err + } + instance.Mutate(inst, tmpl) + return nil }) if err != nil { return ctrl.Result{}, err @@ -73,16 +83,18 @@ func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if op != controllerutil.OperationResultNone { log.Info("instance synced", "instance", inst.Name) r.Recorder.Eventf(&ws, corev1.EventTypeNormal, string(op), "successfully reconciled. instance synced") + } else { + log.Debug().Info("the result of update workspace instance operation is None", "instance", inst.Name) } + gvk, _ := apiutil.GVKForObject(inst, r.Scheme) ws.Status.Instance = cosmov1alpha1.ObjectRef{ ObjectReference: corev1.ObjectReference{ - APIVersion: gvk.GroupVersion().String(), - Kind: gvk.Kind, - Name: inst.GetName(), - Namespace: inst.GetNamespace(), - ResourceVersion: inst.GetResourceVersion(), - UID: inst.GetUID(), + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: inst.GetName(), + Namespace: inst.GetNamespace(), + UID: inst.GetUID(), }, CreationTimestamp: &inst.CreationTimestamp, } @@ -91,7 +103,7 @@ func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( ir := traefikv1.IngressRoute{} ir.SetName(ws.Name) ir.SetNamespace(ws.Namespace) - op, err = kubeutil.CreateOrUpdate(ctx, r.Client, &ir, func() error { + op, err = controllerutil.CreateOrUpdate(ctx, r.Client, &ir, func() error { return r.TraefikIngressRouteCfg.PatchTraefikIngressRouteAsDesired(&ir, ws, r.Scheme) }) if err != nil { @@ -113,7 +125,7 @@ func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( if err := r.Status().Update(ctx, &ws); err != nil { return ctrl.Result{}, err } - log.Debug().Info("status updated") + log.Info("status updated") } log.Debug().Info("finish reconcile") diff --git a/internal/controllers/workspace_controller_test.go b/internal/controllers/workspace_controller_test.go index 25e73b64..3396d894 100644 --- a/internal/controllers/workspace_controller_test.go +++ b/internal/controllers/workspace_controller_test.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/cosmo-workspace/cosmo/pkg/instance" . "github.com/cosmo-workspace/cosmo/pkg/snap" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -217,7 +218,7 @@ spec: for _, v := range createdInst.Spec.Override.PatchesJson6902 { if kubeutil.DeploymentGVK == v.Target.GroupVersionKind() && - v.Target.Name == wsConfig.DeploymentName { + v.Target.Name == instance.InstanceResourceName(wsName, wsConfig.DeploymentName) { return v.Patch } } diff --git a/internal/dashboard/workspace_handler_test.go b/internal/dashboard/workspace_handler_test.go index 317ea6d3..3ae13980 100644 --- a/internal/dashboard/workspace_handler_test.go +++ b/internal/dashboard/workspace_handler_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "net/http" + "time" . "github.com/cosmo-workspace/cosmo/pkg/snap" . "github.com/onsi/ginkgo/v2" @@ -144,6 +145,7 @@ var _ = Describe("Dashboard server [Workspace]", func() { testUtil.CreateWorkspace("admin-user", "ws1", "template1", nil) testUtil.CreateWorkspace("normal-user", "ws1", "template1", map[string]string{"HOGE": "HOGEHOGE"}) testUtil.UpsertNetworkRule("normal-user", "ws1", "add", 18080, "/", false, -1) + time.Sleep(100) By("---------------test start----------------") ctx := context.Background() res, err := client.GetWorkspace(ctx, NewRequestWithSession(req, getSession(loginUser))) @@ -348,6 +350,7 @@ var _ = Describe("Dashboard server [Workspace]", func() { testUtil.UpsertNetworkRule("normal-user", "ws1", "nw1", 9999, "/", false, -1) testUtil.CreateWorkspace("admin-user", "ws1", "template1", map[string]string{}) testUtil.UpsertNetworkRule("admin-user", "ws1", "nw1", 9999, "/", false, -1) + time.Sleep(100) By("---------------test start----------------") ctx := context.Background() res, err := client.DeleteNetworkRule(ctx, NewRequestWithSession(req, getSession(loginUser))) diff --git a/internal/webhooks/instance_webhook.go b/internal/webhooks/instance_webhook.go index d9b7ff5d..c249dc38 100644 --- a/internal/webhooks/instance_webhook.go +++ b/internal/webhooks/instance_webhook.go @@ -87,7 +87,7 @@ func (h *InstanceMutationWebhookHandler) Handle(ctx context.Context, req admissi before := inst.DeepCopyObject().(cosmov1alpha1.InstanceObject) - mutateInstanceObject(inst, tmpl) + instance.Mutate(inst, tmpl) log.Debug().PrintObjectDiff(before, inst) @@ -99,41 +99,6 @@ func (h *InstanceMutationWebhookHandler) Handle(ctx context.Context, req admissi return admission.PatchResponseFromRaw(req.Object.Raw, marshaled) } -func mutateInstanceObject(inst cosmov1alpha1.InstanceObject, tmpl cosmov1alpha1.TemplateObject) { - instSpec := inst.GetSpec() - tmplSpec := tmpl.GetSpec() - - // mutate the fields in instance - // propagate template type annotation to instance annotation - if tmplType, ok := template.GetTemplateType(tmpl); ok { - template.SetTemplateType(inst, tmplType) - } - - // defaulting required vars - for _, v := range tmplSpec.RequiredVars { - found := false - for key := range instSpec.Vars { - if template.FixupTemplateVarKey(key) == template.FixupTemplateVarKey(v.Var) { - found = true - } - } - if !found && v.Default != "" { - if instSpec.Vars == nil { - instSpec.Vars = make(map[string]string) - } - instSpec.Vars[v.Var] = v.Default - } - } - - // update name to instance fixed resource name - patchSpec := instSpec.Override.PatchesJson6902 - for i, p := range patchSpec { - if p.Target.Name != "" && !template.IsDisableNamePrefix(tmpl) { - patchSpec[i].Target.Name = instance.InstanceResourceName(inst.GetName(), p.Target.Name) - } - } -} - func (h *InstanceMutationWebhookHandler) InjectDecoder(d *admission.Decoder) error { h.decoder = d return nil diff --git a/internal/webhooks/__snapshots__/instance_webhook_test.snap b/pkg/instance/__snapshots__/mutate_test.snap similarity index 87% rename from internal/webhooks/__snapshots__/instance_webhook_test.snap rename to pkg/instance/__snapshots__/mutate_test.snap index 2b9c611e..0df7993f 100755 --- a/internal/webhooks/__snapshots__/instance_webhook_test.snap +++ b/pkg/instance/__snapshots__/mutate_test.snap @@ -1,5 +1,5 @@ -[Test_mutateInstanceObject/mutate_workspace_instance - 1] +[TestMutate/mutate_workspace_instance - 1] { "metadata": { "creationTimestamp": null, @@ -38,7 +38,7 @@ } --- -[Test_mutateInstanceObject/mutate_useraddon_clusterinstance - 1] +[TestMutate/mutate_useraddon_clusterinstance - 1] { "metadata": { "creationTimestamp": null, diff --git a/pkg/instance/mutate.go b/pkg/instance/mutate.go new file mode 100644 index 00000000..761ad2d3 --- /dev/null +++ b/pkg/instance/mutate.go @@ -0,0 +1,41 @@ +package instance + +import ( + cosmov1alpha1 "github.com/cosmo-workspace/cosmo/api/v1alpha1" + "github.com/cosmo-workspace/cosmo/pkg/template" +) + +func Mutate(inst cosmov1alpha1.InstanceObject, tmpl cosmov1alpha1.TemplateObject) { + instSpec := inst.GetSpec() + tmplSpec := tmpl.GetSpec() + + // mutate the fields in instance + // propagate template type annotation to instance annotation + if tmplType, ok := template.GetTemplateType(tmpl); ok { + template.SetTemplateType(inst, tmplType) + } + + // defaulting required vars + for _, v := range tmplSpec.RequiredVars { + found := false + for key := range instSpec.Vars { + if template.FixupTemplateVarKey(key) == template.FixupTemplateVarKey(v.Var) { + found = true + } + } + if !found && v.Default != "" { + if instSpec.Vars == nil { + instSpec.Vars = make(map[string]string) + } + instSpec.Vars[v.Var] = v.Default + } + } + + // update name to instance fixed resource name + patchSpec := instSpec.Override.PatchesJson6902 + for i, p := range patchSpec { + if p.Target.Name != "" && !template.IsDisableNamePrefix(tmpl) { + patchSpec[i].Target.Name = InstanceResourceName(inst.GetName(), p.Target.Name) + } + } +} diff --git a/internal/webhooks/instance_webhook_test.go b/pkg/instance/mutate_test.go similarity index 95% rename from internal/webhooks/instance_webhook_test.go rename to pkg/instance/mutate_test.go index af587ea5..b609c74e 100644 --- a/internal/webhooks/instance_webhook_test.go +++ b/pkg/instance/mutate_test.go @@ -1,4 +1,4 @@ -package webhooks +package instance import ( "testing" @@ -10,7 +10,7 @@ import ( cosmov1alpha1 "github.com/cosmo-workspace/cosmo/api/v1alpha1" ) -func Test_mutateInstanceObject(t *testing.T) { +func TestMutate(t *testing.T) { type args struct { inst cosmov1alpha1.InstanceObject tmpl cosmov1alpha1.TemplateObject @@ -106,7 +106,7 @@ func Test_mutateInstanceObject(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mutateInstanceObject(tt.args.inst, tt.args.tmpl) + Mutate(tt.args.inst, tt.args.tmpl) snaps.MatchJSON(t, tt.args.inst) }) } diff --git a/pkg/kubeutil/client.go b/pkg/kubeutil/client.go index af06c47e..9788e01d 100644 --- a/pkg/kubeutil/client.go +++ b/pkg/kubeutil/client.go @@ -2,16 +2,12 @@ package kubeutil import ( "context" - "fmt" - "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) func Apply(ctx context.Context, c client.Client, obj *unstructured.Unstructured, fieldManager string, dryrun, force bool) (patched *unstructured.Unstructured, err error) { @@ -41,45 +37,3 @@ func GetUnstructured(ctx context.Context, c client.Client, gvk schema.GroupVersi } return &obj, nil } - -var CreateOrUpdate = controllerutil.CreateOrUpdate - -func DryrunCreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f controllerutil.MutateFn) (controllerutil.OperationResult, error) { - key := client.ObjectKeyFromObject(obj) - if err := c.Get(ctx, key, obj); err != nil { - if !apierrors.IsNotFound(err) { - return controllerutil.OperationResultNone, err - } - if err := mutate(f, key, obj); err != nil { - return controllerutil.OperationResultNone, err - } - if err := c.Create(ctx, obj, client.DryRunAll); err != nil { - return controllerutil.OperationResultNone, err - } - return controllerutil.OperationResultCreated, nil - } - - existing := obj.DeepCopyObject() //nolint - if err := mutate(f, key, obj); err != nil { - return controllerutil.OperationResultNone, err - } - - if equality.Semantic.DeepEqual(existing, obj) { - return controllerutil.OperationResultNone, nil - } - - if err := c.Update(ctx, obj, client.DryRunAll); err != nil { - return controllerutil.OperationResultNone, err - } - return controllerutil.OperationResultUpdated, nil -} - -func mutate(f controllerutil.MutateFn, key client.ObjectKey, obj client.Object) error { - if err := f(); err != nil { - return err - } - if newKey := client.ObjectKeyFromObject(obj); key != newKey { - return fmt.Errorf("MutateFn cannot mutate object name and/or object namespace") - } - return nil -} diff --git a/pkg/snap/object_snapshot.go b/pkg/snap/object_snapshot.go index 1c2a0bb9..c1ffce55 100644 --- a/pkg/snap/object_snapshot.go +++ b/pkg/snap/object_snapshot.go @@ -26,10 +26,12 @@ func UserSnapshot(in *cosmov1alpha1.User) *cosmov1alpha1.User { obj.Status.Addons[i] = v } sort.Slice(obj.Status.Addons, func(i, j int) bool { - return obj.Status.Addons[i].Kind < obj.Status.Addons[j].Kind - }) - sort.Slice(obj.Status.Addons, func(i, j int) bool { - return obj.Status.Addons[i].Name < obj.Status.Addons[j].Name + x, y := obj.Status.Addons[i], obj.Status.Addons[j] + if x.Kind != y.Kind { + return x.Kind < y.Kind + } else { + return x.Name < y.Name + } }) return obj @@ -47,10 +49,12 @@ func InstanceSnapshot(in cosmov1alpha1.InstanceObject) cosmov1alpha1.InstanceObj obj.GetStatus().LastApplied[i] = v } sort.Slice(obj.GetStatus().LastApplied, func(i, j int) bool { - return obj.GetStatus().LastApplied[i].Kind < obj.GetStatus().LastApplied[j].Kind - }) - sort.Slice(obj.GetStatus().LastApplied, func(i, j int) bool { - return obj.GetStatus().LastApplied[i].Name < obj.GetStatus().LastApplied[j].Name + x, y := obj.GetStatus().LastApplied[i], obj.GetStatus().LastApplied[j] + if x.Kind != y.Kind { + return x.Kind < y.Kind + } else { + return x.Name < y.Name + } }) obj.GetStatus().TemplateResourceVersion = "" diff --git a/pkg/useraddon/useraddon.go b/pkg/useraddon/useraddon.go index 6494db89..bcbf5b46 100644 --- a/pkg/useraddon/useraddon.go +++ b/pkg/useraddon/useraddon.go @@ -70,13 +70,16 @@ func PatchUserAddonInstanceAsDesired(inst cosmov1alpha1.InstanceObject, addon co inst.GetSpec().Template = cosmov1alpha1.TemplateRef{Name: EmptyTemplateObject(addon).GetName()} // add default vars + var vars map[string]string if addon.Vars == nil { - addon.Vars = make(map[string]string) + vars = make(map[string]string) + } else { + vars = copyMap(addon.Vars) } - addon.Vars[template.DefaultVarsNamespace] = cosmov1alpha1.UserNamespace(user.Name) - addon.Vars[cosmov1alpha1.TemplateVarUser] = user.Name - addon.Vars[cosmov1alpha1.TemplateVarUserName] = user.Name - inst.GetSpec().Vars = addon.Vars + vars[template.DefaultVarsNamespace] = cosmov1alpha1.UserNamespace(user.Name) + vars[cosmov1alpha1.TemplateVarUser] = user.Name + vars[cosmov1alpha1.TemplateVarUserName] = user.Name + inst.GetSpec().Vars = vars // set owner reference if scheme is not nil if scheme != nil { @@ -88,3 +91,13 @@ func PatchUserAddonInstanceAsDesired(inst cosmov1alpha1.InstanceObject, addon co return nil } + +// TODO use maps in Go 1.21 instead +func copyMap(m map[string]string) map[string]string { + m2 := make(map[string]string) + + for key, value := range m { + m2[key] = value + } + return m2 +} diff --git a/pkg/workspace/workspace.go b/pkg/workspace/workspace.go index 691a1850..a92a2d60 100644 --- a/pkg/workspace/workspace.go +++ b/pkg/workspace/workspace.go @@ -33,7 +33,7 @@ func PatchWorkspaceInstanceAsDesired(inst *cosmov1alpha1.Instance, ws cosmov1alp inst.Spec = cosmov1alpha1.InstanceSpec{ Template: ws.Spec.Template, - Vars: addWorkspaceDefaultVars(ws.Spec.Vars, ws), + Vars: varsWithWorkspaceDefault(ws), Override: cosmov1alpha1.OverrideSpec{ PatchesJson6902: []cosmov1alpha1.Json6902{ { @@ -85,11 +85,14 @@ func svcPorts(netRules []cosmov1alpha1.NetworkRule) []corev1.ServicePort { return ports } -func addWorkspaceDefaultVars(vars map[string]string, ws cosmov1alpha1.Workspace) map[string]string { +func varsWithWorkspaceDefault(ws cosmov1alpha1.Workspace) map[string]string { user := cosmov1alpha1.UserNameByNamespace(ws.GetNamespace()) - if vars == nil { + var vars map[string]string + if ws.Spec.Vars == nil { vars = make(map[string]string) + } else { + vars = copyMap(ws.Spec.Vars) } // urlvar vars[cosmov1alpha1.URLVarWorkspaceName] = ws.GetName() @@ -101,3 +104,13 @@ func addWorkspaceDefaultVars(vars map[string]string, ws cosmov1alpha1.Workspace) return vars } + +// TODO use maps in Go 1.21 instead +func copyMap(m map[string]string) map[string]string { + m2 := make(map[string]string) + + for key, value := range m { + m2[key] = value + } + return m2 +}