From 0350ee7cad378f404578d39673853109246b8b6f Mon Sep 17 00:00:00 2001 From: Vadim Gedz Date: Mon, 10 Jun 2024 22:12:21 +0300 Subject: [PATCH] feat(updater): add fire and forget mode (#309) --- .../argocd/argo_status_updater.go | 8 ++++ docs/git-integration.md | 10 ++++ internal/models/argo.go | 9 ++++ internal/models/argo_test.go | 46 +++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/cmd/argo-watcher/argocd/argo_status_updater.go b/cmd/argo-watcher/argocd/argo_status_updater.go index ef0746af..59ce177d 100644 --- a/cmd/argo-watcher/argocd/argo_status_updater.go +++ b/cmd/argo-watcher/argocd/argo_status_updater.go @@ -95,6 +95,9 @@ func (updater *ArgoStatusUpdater) WaitForRollout(task models.Task) { // get application status status := application.GetRolloutStatus(task.ListImages(), updater.registryProxyUrl, updater.acceptSuspended) + if application.IsFireAndForgetModeActive() { + status = models.ArgoRolloutAppSuccess + } if status == models.ArgoRolloutAppSuccess { log.Info().Str("id", task.Id).Msg("App is running on the expected version.") // deployment success @@ -213,6 +216,11 @@ func (updater *ArgoStatusUpdater) waitRollout(task models.Task) (*models.Applica _ = retry.Do(func() error { application, err = updater.argo.api.GetApplication(task.App) + if application.IsFireAndForgetModeActive() { + log.Debug().Str("id", task.Id).Msg("Fire and forge mode is active, skipping checks...") + return nil + } + if err != nil { // check if ArgoCD didn't have the app if task.IsAppNotFoundError(err) { diff --git a/docs/git-integration.md b/docs/git-integration.md index 8bf81708..fb21a8ae 100644 --- a/docs/git-integration.md +++ b/docs/git-integration.md @@ -78,6 +78,16 @@ sandbox/charts/demo/.argocd-source-demo.yaml > This is not an ideal solution, but so far it is the only way to reliably determine the correct override file to update. +### Fire and forget mode + +In rare cases, we need to force update an application without waiting for image to be detected by ArgoCD as a running. For example for Applications with `CronJob` only. + +The following annotation will force argo-watcher to just update the image, and consider application `deployed`: + +```bash +argo-watcher/fire-and-forget: "true" +``` + ### Additional information - The `app` alias is intended to correspond with an alias specified in the `argo-watcher/ALIAS.helm.image-tag` annotation. diff --git a/internal/models/argo.go b/internal/models/argo.go index c8f16636..d0aea718 100644 --- a/internal/models/argo.go +++ b/internal/models/argo.go @@ -25,6 +25,7 @@ const ( managedGitBranch = "argo-watcher/write-back-branch" managedGitPath = "argo-watcher/write-back-path" managedGitFile = "argo-watcher/write-back-filename" + fireAndForgetAnnotation = "argo-watcher/fire-and-forget" ) type ApplicationOperationResource struct { @@ -286,6 +287,14 @@ func (app *Application) UpdateGitImageTag(task *Task) error { return nil } +// IsFireAndForgetModeActive checks if 'fire-and-forget' mode is enabled in Application's annotations. +func (app *Application) IsFireAndForgetModeActive() bool { + if app.Metadata.Annotations == nil { + return false + } + return app.Metadata.Annotations[fireAndForgetAnnotation] == "true" +} + // extractManagedImages extracts the managed images from the application's annotations. // It returns a map of the managed images, where the key is the application alias and the value is the image name. func extractManagedImages(annotations map[string]string) (map[string]string, error) { diff --git a/internal/models/argo_test.go b/internal/models/argo_test.go index 369bf815..b7dcaacf 100644 --- a/internal/models/argo_test.go +++ b/internal/models/argo_test.go @@ -386,3 +386,49 @@ func TestExtractManagedImages(t *testing.T) { }) } } + +func TestIsFireAndForgetModeActive(t *testing.T) { + tt := []struct { + name string + app Application + want bool + }{ + { + name: "FireAndForget mode active", + app: Application{ + Metadata: ApplicationMetadata{ + Annotations: map[string]string{ + fireAndForgetAnnotation: "true", + }, + }, + }, + want: true, + }, + { + name: "FireAndForget mode inactive", + app: Application{ + Metadata: ApplicationMetadata{ + Annotations: map[string]string{ + fireAndForgetAnnotation: "false", + }, + }, + }, + want: false, + }, + { + name: "Annotations are nil", + app: Application{ + Metadata: ApplicationMetadata{ + Annotations: nil, + }, + }, + want: false, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.want, tc.app.IsFireAndForgetModeActive()) + }) + } +}