Add option to skip the dryrun from the sync context (#708)

* Add option to skip the dryrun from the sync context

Signed-off-by: Nick Heijmink <nick.heijmink@alliander.com>

* Fix test by mocking the discovery

Signed-off-by: Nick Heijmink <nick.heijmink@alliander.com>

* Fix linting errors

Signed-off-by: Nick Heijmink <nick.heijmink@alliander.com>

* Fix skip dryrun const

---------

Signed-off-by: Nick Heijmink <nick.heijmink@alliander.com>
This commit is contained in:
Nick Heijmink 2025-04-10 23:12:36 +02:00 committed by GitHub
parent c61756277b
commit 717b8bfd69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 63 additions and 12 deletions

View File

@ -20,6 +20,8 @@ const (
// Sync option that disables dry run in resource is missing in the cluster
SyncOptionSkipDryRunOnMissingResource = "SkipDryRunOnMissingResource=true"
// Sync option that disables dry run for applying resources
SyncOptionSkipDryRun = "SkipDryRun=true"
// Sync option that disables resource pruning
SyncOptionDisablePrune = "Prune=false"
// Sync option that disables resource validation

View File

@ -80,7 +80,8 @@ that runs before all other resources. The `argocd.argoproj.io/sync-wave` annotat
The sync options allows customizing the synchronization of selected resources. The options are specified using the
annotation 'argocd.argoproj.io/sync-options'. Following sync options are supported:
- SkipDryRunOnMissingResource=true - disables dry run in resource is missing in the cluster
- SkipDryRunOnMissingResource=true - disables dry run if CRD resource is missing in the cluster
- SkipDryRun=true - disables dry run if resource is missing in the cluster
- Prune=false - disables resource pruning
- Validate=false - disables resource validation (equivalent to 'kubectl apply --validate=false')

View File

@ -188,6 +188,12 @@ func WithReplace(replace bool) SyncOpt {
}
}
func WithSkipDryRun(skipDryRun bool) SyncOpt {
return func(ctx *syncContext) {
ctx.skipDryRun = skipDryRun
}
}
func WithServerSideApply(serverSideApply bool) SyncOpt {
return func(ctx *syncContext) {
ctx.serverSideApply = serverSideApply
@ -335,6 +341,7 @@ type syncContext struct {
namespace string
dryRun bool
skipDryRun bool
force bool
validate bool
skipHooks bool
@ -812,16 +819,24 @@ func (sc *syncContext) getSyncTasks() (_ syncTasks, successful bool) {
}
if err != nil {
// Special case for custom resources: if CRD is not yet known by the K8s API server,
// and the CRD is part of this sync or the resource is annotated with SkipDryRunOnMissingResource=true,
// then skip verification during `kubectl apply --dry-run` since we expect the CRD
// to be created during app synchronization.
if apierrors.IsNotFound(err) &&
switch {
case apierrors.IsNotFound(err) &&
((task.targetObj != nil && resourceutil.HasAnnotationOption(task.targetObj, common.AnnotationSyncOptions, common.SyncOptionSkipDryRunOnMissingResource)) ||
sc.hasCRDOfGroupKind(task.group(), task.kind())) {
sc.hasCRDOfGroupKind(task.group(), task.kind())):
// Special case for custom resources: if CRD is not yet known by the K8s API server,
// and the CRD is part of this sync or the resource is annotated with SkipDryRunOnMissingResource=true,
// then skip verification during `kubectl apply --dry-run` since we expect the CRD
// to be created during app synchronization.
sc.log.WithValues("task", task).V(1).Info("Skip dry-run for custom resource")
task.skipDryRun = true
} else {
case sc.skipDryRun:
// Skip dryrun for task if the sync context is in skip dryrun mode
// This can be useful when resource creation is depending on the creation of other resources
// like namespaces that need to be created first before the resources in the namespace can be created
// For CRD's one can also use the SkipDryRunOnMissingResource annotation.
sc.log.WithValues("task", task).V(1).Info("Skipping dry-run for task because skipDryRun is set in the sync context")
task.skipDryRun = true
default:
sc.setResourceResult(task, common.ResultCodeSyncFailed, "", err.Error())
successful = false
}

View File

@ -1189,6 +1189,39 @@ func TestNamespaceAutoCreationForNonExistingNs(t *testing.T) {
waveOverride: nil,
}, tasks[0])
})
t.Run("pre-sync task error should be ignored if skip dryrun is true", func(t *testing.T) {
syncCtx.resources = groupResources(ReconciliationResult{
Live: []*unstructured.Unstructured{nil},
Target: []*unstructured.Unstructured{pod},
})
fakeDisco := syncCtx.disco.(*fakedisco.FakeDiscovery)
fakeDisco.Resources = []*metav1.APIResourceList{}
syncCtx.disco = fakeDisco
syncCtx.skipDryRun = true
creatorCalled := false
syncCtx.syncNamespace = func(_, _ *unstructured.Unstructured) (bool, error) {
creatorCalled = true
return true, errors.New("some error")
}
tasks, successful := syncCtx.getSyncTasks()
assert.True(t, creatorCalled)
assert.True(t, successful)
assert.Len(t, tasks, 2)
assert.Equal(t, &syncTask{
phase: synccommon.SyncPhasePreSync,
liveObj: nil,
targetObj: tasks[0].targetObj,
skipDryRun: true,
syncStatus: synccommon.ResultCodeSyncFailed,
operationState: synccommon.OperationError,
message: "namespaceModifier error: some error",
waveOverride: nil,
}, tasks[0])
})
}
func createNamespaceTask(namespace string) (*syncTask, error) {

View File

@ -106,7 +106,7 @@ func (r *MockResourceOps) GetLastResourceCommand(key kube.ResourceKey) string {
return r.lastCommandPerResource[key]
}
func (r *MockResourceOps) ApplyResource(_ context.Context, obj *unstructured.Unstructured, _ cmdutil.DryRunStrategy, force, validate, serverSideApply bool, manager string) (string, error) {
func (r *MockResourceOps) ApplyResource(_ context.Context, obj *unstructured.Unstructured, _ cmdutil.DryRunStrategy, force bool, validate bool, serverSideApply bool, manager string) (string, error) {
r.SetLastValidate(validate)
r.SetLastServerSideApply(serverSideApply)
r.SetLastServerSideApplyManager(manager)

View File

@ -39,7 +39,7 @@ import (
// ResourceOperations provides methods to manage k8s resources
type ResourceOperations interface {
ApplyResource(ctx context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, force, validate, serverSideApply bool, manager string) (string, error)
ApplyResource(ctx context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, force bool, validate bool, serverSideApply bool, manager string) (string, error)
ReplaceResource(ctx context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, force bool) (string, error)
CreateResource(ctx context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, validate bool) (string, error)
UpdateResource(ctx context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy) (*unstructured.Unstructured, error)
@ -301,7 +301,7 @@ func (k *kubectlResourceOperations) UpdateResource(ctx context.Context, obj *uns
}
// ApplyResource performs an apply of a unstructured resource
func (k *kubectlServerSideDiffDryRunApplier) ApplyResource(_ context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, force, validate, serverSideApply bool, manager string) (string, error) {
func (k *kubectlServerSideDiffDryRunApplier) ApplyResource(_ context.Context, obj *unstructured.Unstructured, dryRunStrategy cmdutil.DryRunStrategy, force bool, validate bool, serverSideApply bool, manager string) (string, error) {
span := k.tracer.StartSpan("ApplyResource")
span.SetBaggageItem("kind", obj.GetKind())
span.SetBaggageItem("name", obj.GetName())
@ -357,7 +357,7 @@ func (k *kubectlResourceOperations) ApplyResource(ctx context.Context, obj *unst
})
}
func newApplyOptionsCommon(config *rest.Config, fact cmdutil.Factory, ioStreams genericclioptions.IOStreams, obj *unstructured.Unstructured, fileName string, validate bool, force, serverSideApply bool, dryRunStrategy cmdutil.DryRunStrategy, manager string) (*apply.ApplyOptions, error) {
func newApplyOptionsCommon(config *rest.Config, fact cmdutil.Factory, ioStreams genericclioptions.IOStreams, obj *unstructured.Unstructured, fileName string, validate bool, force bool, serverSideApply bool, dryRunStrategy cmdutil.DryRunStrategy, manager string) (*apply.ApplyOptions, error) {
flags := apply.NewApplyFlags(ioStreams)
o := &apply.ApplyOptions{
IOStreams: ioStreams,