From 870d3ba92372c2edbf65936fa2f8b62289e4dbef Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Fri, 19 Jan 2024 13:11:57 +0530 Subject: [PATCH 1/7] Revert "feat: retry with client side dry run if server one was failed (#548)" This reverts commit c0c2dd1f6f487c46a25d3d3caaad7fec06667728. Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/sync/sync_context.go | 50 ++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/pkg/sync/sync_context.go b/pkg/sync/sync_context.go index c2ed67691..fd03d11b3 100644 --- a/pkg/sync/sync_context.go +++ b/pkg/sync/sync_context.go @@ -921,39 +921,29 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (c var err error var message string shouldReplace := sc.replace || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionReplace) - applyFn := func(dryRunStrategy cmdutil.DryRunStrategy) (string, error) { - if !shouldReplace { - return sc.resourceOps.ApplyResource(context.TODO(), t.targetObj, dryRunStrategy, force, validate, serverSideApply, sc.serverSideApplyManager, false) - } - if t.liveObj == nil { - return sc.resourceOps.CreateResource(context.TODO(), t.targetObj, dryRunStrategy, validate) - } - // Avoid using `kubectl replace` for CRDs since 'replace' might recreate resource and so delete all CRD instances. - // The same thing applies for namespaces, which would delete the namespace as well as everything within it, - // so we want to avoid using `kubectl replace` in that case as well. - if kube.IsCRD(t.targetObj) || t.targetObj.GetKind() == kubeutil.NamespaceKind { - update := t.targetObj.DeepCopy() - update.SetResourceVersion(t.liveObj.GetResourceVersion()) - _, err = sc.resourceOps.UpdateResource(context.TODO(), update, dryRunStrategy) - if err != nil { - return fmt.Sprintf("error when updating: %v", err.Error()), err + if shouldReplace { + if t.liveObj != nil { + // Avoid using `kubectl replace` for CRDs since 'replace' might recreate resource and so delete all CRD instances. + // The same thing applies for namespaces, which would delete the namespace as well as everything within it, + // so we want to avoid using `kubectl replace` in that case as well. + if kube.IsCRD(t.targetObj) || t.targetObj.GetKind() == kubeutil.NamespaceKind { + update := t.targetObj.DeepCopy() + update.SetResourceVersion(t.liveObj.GetResourceVersion()) + _, err = sc.resourceOps.UpdateResource(context.TODO(), update, dryRunStrategy) + if err == nil { + message = fmt.Sprintf("%s/%s updated", t.targetObj.GetKind(), t.targetObj.GetName()) + } else { + message = fmt.Sprintf("error when updating: %v", err.Error()) + } + } else { + message, err = sc.resourceOps.ReplaceResource(context.TODO(), t.targetObj, dryRunStrategy, force) } - return fmt.Sprintf("%s/%s updated", t.targetObj.GetKind(), t.targetObj.GetName()), nil - + } else { + message, err = sc.resourceOps.CreateResource(context.TODO(), t.targetObj, dryRunStrategy, validate) } - return sc.resourceOps.ReplaceResource(context.TODO(), t.targetObj, dryRunStrategy, force) - } - - message, err = applyFn(dryRunStrategy) - - // DryRunServer fails with "Kind does not support fieldValidation" error for kubernetes server < 1.25 - // it fails inside apply.go , o.DryRunVerifier.HasSupport(info.Mapping.GroupVersionKind) line - // so we retry with DryRunClient that works for all cases, but cause issues with hooks - // Details: https://github.com/argoproj/argo-cd/issues/16177 - if dryRunStrategy == cmdutil.DryRunServer && err != nil { - message, err = applyFn(cmdutil.DryRunClient) + } else { + message, err = sc.resourceOps.ApplyResource(context.TODO(), t.targetObj, dryRunStrategy, force, validate, serverSideApply, sc.serverSideApplyManager, false) } - if err != nil { return common.ResultCodeSyncFailed, err.Error() } From e7dc823408ad5fa1a7b7baf2250a044ce3db90c1 Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Fri, 19 Jan 2024 13:12:15 +0530 Subject: [PATCH 2/7] Revert "fix(server): use server side dry run in case if it is server side apply (#546)" This reverts commit 4a5648ee411b42f6601782ae6c1c50e0f724c9c5. Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/sync/sync_context.go | 18 +++++------------- pkg/sync/sync_context_test.go | 20 -------------------- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/pkg/sync/sync_context.go b/pkg/sync/sync_context.go index fd03d11b3..0262b512f 100644 --- a/pkg/sync/sync_context.go +++ b/pkg/sync/sync_context.go @@ -903,24 +903,16 @@ func (sc *syncContext) ensureCRDReady(name string) error { }) } -func getDryRunStrategy(serverSideApply, dryRun bool) cmdutil.DryRunStrategy { - if !dryRun { - return cmdutil.DryRunNone - } - if serverSideApply { - return cmdutil.DryRunServer - } - return cmdutil.DryRunClient -} - func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (common.ResultCode, string) { - serverSideApply := sc.serverSideApply || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionServerSideApply) - - dryRunStrategy := getDryRunStrategy(serverSideApply, dryRun) + dryRunStrategy := cmdutil.DryRunNone + if dryRun { + dryRunStrategy = cmdutil.DryRunClient + } var err error var message string shouldReplace := sc.replace || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionReplace) + serverSideApply := sc.serverSideApply || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionServerSideApply) if shouldReplace { if t.liveObj != nil { // Avoid using `kubectl replace` for CRDs since 'replace' might recreate resource and so delete all CRD instances. diff --git a/pkg/sync/sync_context_test.go b/pkg/sync/sync_context_test.go index 984787014..0c2e1e14c 100644 --- a/pkg/sync/sync_context_test.go +++ b/pkg/sync/sync_context_test.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "k8s.io/kubectl/pkg/cmd/util" "net/http" "net/http/httptest" "reflect" @@ -540,25 +539,6 @@ func (s *APIServerMock) newHttpServer(t *testing.T, apiFailuresCount int) *httpt return server } -func TestGetDryRunStrategy(t *testing.T) { - t.Run("no dry run", func(t *testing.T) { - strategy := getDryRunStrategy(false, false) - assert.Equal(t, util.DryRunNone, strategy) - }) - t.Run("no dry run with server side apply", func(t *testing.T) { - strategy := getDryRunStrategy(true, false) - assert.Equal(t, util.DryRunNone, strategy) - }) - t.Run("dry run with server side apply", func(t *testing.T) { - strategy := getDryRunStrategy(true, true) - assert.Equal(t, util.DryRunServer, strategy) - }) - t.Run("dry run with client side apply", func(t *testing.T) { - strategy := getDryRunStrategy(false, true) - assert.Equal(t, util.DryRunClient, strategy) - }) -} - func TestServerResourcesRetry(t *testing.T) { type fixture struct { apiServerMock *APIServerMock From 208a954e637fa0c24d0935b6e8ead876d71668b3 Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Fri, 19 Jan 2024 19:31:53 +0530 Subject: [PATCH 3/7] Fixed the logic to disable server side apply if it is a dry run Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/sync/sync_context.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/sync/sync_context.go b/pkg/sync/sync_context.go index 0262b512f..26d09efcb 100644 --- a/pkg/sync/sync_context.go +++ b/pkg/sync/sync_context.go @@ -906,13 +906,18 @@ func (sc *syncContext) ensureCRDReady(name string) error { func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (common.ResultCode, string) { dryRunStrategy := cmdutil.DryRunNone if dryRun { + // irrespective of the dry run mode set in the sync context, always run + // in client dry run mode as the goal is to validate only the + // yaml correctness of the rendered manifests. dryRunStrategy = cmdutil.DryRunClient } var err error var message string shouldReplace := sc.replace || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionReplace) - serverSideApply := sc.serverSideApply || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionServerSideApply) + // if it is a dry run, disable server side apply, as the goal is to validate only the + // yaml correctness of the rendered manifests. + serverSideApply := !dryRun && (sc.serverSideApply || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionServerSideApply)) if shouldReplace { if t.liveObj != nil { // Avoid using `kubectl replace` for CRDs since 'replace' might recreate resource and so delete all CRD instances. From 209eee8427ee9fce1ad30117642c563fe5e6e5f0 Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Fri, 19 Jan 2024 22:41:11 +0530 Subject: [PATCH 4/7] Added more values in the log message for better debugging Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/utils/kube/resource_ops.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/utils/kube/resource_ops.go b/pkg/utils/kube/resource_ops.go index 7faa037b2..5fd23930f 100644 --- a/pkg/utils/kube/resource_ops.go +++ b/pkg/utils/kube/resource_ops.go @@ -229,7 +229,12 @@ func (k *kubectlResourceOperations) ApplyResource(ctx context.Context, obj *unst span.SetBaggageItem("kind", obj.GetKind()) span.SetBaggageItem("name", obj.GetName()) defer span.Finish() - k.log.Info(fmt.Sprintf("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), k.config.Host, obj.GetNamespace())) + k.log.WithValues( + "dry-run", dryRunStrategy, + "manager", manager, + "serverSideApply", serverSideApply, + "serverSideDiff", serverSideDiff + ).Info(fmt.Sprintf("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), k.config.Host, obj.GetNamespace())) return k.runResourceCommand(ctx, obj, dryRunStrategy, func(f cmdutil.Factory, ioStreams genericclioptions.IOStreams, fileName string) error { cleanup, err := k.processKubectlRun("apply") if err != nil { From fa36dc3d4d960dae5218a3935af6afd6c1a101f6 Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Fri, 19 Jan 2024 22:50:02 +0530 Subject: [PATCH 5/7] Fixed compilation error Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/utils/kube/resource_ops.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/utils/kube/resource_ops.go b/pkg/utils/kube/resource_ops.go index 5fd23930f..df028f5dd 100644 --- a/pkg/utils/kube/resource_ops.go +++ b/pkg/utils/kube/resource_ops.go @@ -233,8 +233,7 @@ func (k *kubectlResourceOperations) ApplyResource(ctx context.Context, obj *unst "dry-run", dryRunStrategy, "manager", manager, "serverSideApply", serverSideApply, - "serverSideDiff", serverSideDiff - ).Info(fmt.Sprintf("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), k.config.Host, obj.GetNamespace())) + "serverSideDiff", serverSideDiff).Info(fmt.Sprintf("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), k.config.Host, obj.GetNamespace())) return k.runResourceCommand(ctx, obj, dryRunStrategy, func(f cmdutil.Factory, ioStreams genericclioptions.IOStreams, fileName string) error { cleanup, err := k.processKubectlRun("apply") if err != nil { From 2a503268f83c55fa2ab8568c9dfda8f94bb6b2be Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Sat, 20 Jan 2024 00:00:51 +0530 Subject: [PATCH 6/7] Written an inline fn to get string value of dry-run strategy Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/utils/kube/resource_ops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/kube/resource_ops.go b/pkg/utils/kube/resource_ops.go index df028f5dd..6867ae876 100644 --- a/pkg/utils/kube/resource_ops.go +++ b/pkg/utils/kube/resource_ops.go @@ -230,7 +230,7 @@ func (k *kubectlResourceOperations) ApplyResource(ctx context.Context, obj *unst span.SetBaggageItem("name", obj.GetName()) defer span.Finish() k.log.WithValues( - "dry-run", dryRunStrategy, + "dry-run", [...]string{"none", "client", "server"}[dryRunStrategy], "manager", manager, "serverSideApply", serverSideApply, "serverSideDiff", serverSideDiff).Info(fmt.Sprintf("Applying resource %s/%s in cluster: %s, namespace: %s", obj.GetKind(), obj.GetName(), k.config.Host, obj.GetNamespace())) From b77faaaa04a3da4567974c37358b3bdb8e42369d Mon Sep 17 00:00:00 2001 From: Anand Francis Joseph <anjoseph@redhat.com> Date: Sat, 20 Jan 2024 00:20:53 +0530 Subject: [PATCH 7/7] Added comment as requested with reference to the issue number Signed-off-by: Anand Francis Joseph <anjoseph@redhat.com> --- pkg/sync/sync_context.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/sync/sync_context.go b/pkg/sync/sync_context.go index 26d09efcb..c11ff7e0c 100644 --- a/pkg/sync/sync_context.go +++ b/pkg/sync/sync_context.go @@ -909,6 +909,8 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (c // irrespective of the dry run mode set in the sync context, always run // in client dry run mode as the goal is to validate only the // yaml correctness of the rendered manifests. + // running dry-run in server mode breaks the auto create namespace feature + // https://github.com/argoproj/argo-cd/issues/13874 dryRunStrategy = cmdutil.DryRunClient } @@ -917,6 +919,8 @@ func (sc *syncContext) applyObject(t *syncTask, dryRun, force, validate bool) (c shouldReplace := sc.replace || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionReplace) // if it is a dry run, disable server side apply, as the goal is to validate only the // yaml correctness of the rendered manifests. + // running dry-run in server mode breaks the auto create namespace feature + // https://github.com/argoproj/argo-cd/issues/13874 serverSideApply := !dryRun && (sc.serverSideApply || resourceutil.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, common.SyncOptionServerSideApply)) if shouldReplace { if t.liveObj != nil {