Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #1995 - Add maintenance windows for auto synced applications #1

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions assets/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3200,6 +3200,20 @@
}
}
},
"v1alpha1MaintenanceWindow": {
"type": "object",
"title": "MaintenanceWindow controls when automated syncs should be disabled",
"properties": {
"duration": {
"type": "string",
"title": "Duration is the amount of time the maintenance window will be open"
},
"schedule": {
"type": "string",
"title": "Schedule is the time the window will begin"
}
}
},
"v1alpha1Operation": {
"description": "Operation contains requested operation parameters.",
"type": "object",
Expand Down Expand Up @@ -3788,6 +3802,13 @@
"type": "object",
"title": "SyncPolicyAutomated controls the behavior of an automated sync",
"properties": {
"maintenanceWindows": {
"type": "array",
"title": "MaintenanceWindows disables auto-syncing during the configured time windows (default: false)",
"items": {
"$ref": "#/definitions/v1alpha1MaintenanceWindow"
}
},
"prune": {
"type": "boolean",
"format": "boolean",
Expand Down
23 changes: 20 additions & 3 deletions cmd/argocd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string) {
syncPolicy = "<none>"
}
fmt.Printf(printOpFmtStr, "Sync Policy:", syncPolicy)
fmt.Printf(printOpFmtStr, "Maintenance:", app.Spec.SyncPolicy.Automated.ListMaintenanceWindows())
syncStatusStr := string(app.Status.Sync.Status)
switch app.Status.Sync.Status {
case argoappv1.SyncStatusCodeSynced:
Expand Down Expand Up @@ -447,6 +448,18 @@ func setAppOptions(flags *pflag.FlagSet, app *argoappv1.Application, appOpts *ap
}
app.Spec.SyncPolicy.Automated.Prune = appOpts.autoPrune
}
if flags.Changed("self-heal") {
if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil {
log.Fatal("Cannot set --self-heal: application not configured with automatic sync")
}
app.Spec.SyncPolicy.Automated.SelfHeal = appOpts.selfHeal
}
if flags.Changed("maintenance-windows") {
if app.Spec.SyncPolicy == nil || app.Spec.SyncPolicy.Automated == nil {
log.Fatal("Cannot set --maintenance-windows: application not configured with automatic sync")
}
app.Spec.SyncPolicy.Automated.AddMaintenanceWindows(appOpts.maintenanceWindows)
}

return visited
}
Expand Down Expand Up @@ -570,12 +583,14 @@ type appOptions struct {
project string
syncPolicy string
autoPrune bool
selfHeal bool
namePrefix string
directoryRecurse bool
configManagementPlugin string
jsonnetTlaStr []string
jsonnetTlaCode []string
kustomizeImages []string
maintenanceWindows string
}

func addAppFlags(command *cobra.Command, opts *appOptions) {
Expand All @@ -593,12 +608,14 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
command.Flags().StringVar(&opts.project, "project", "", "Application project name")
command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: automated, none)")
command.Flags().BoolVar(&opts.autoPrune, "auto-prune", false, "Set automatic pruning when sync is automated")
command.Flags().BoolVar(&opts.selfHeal, "self-heal", false, "Set self healing when sync is automated")
command.Flags().StringVar(&opts.namePrefix, "nameprefix", "", "Kustomize nameprefix")
command.Flags().BoolVar(&opts.directoryRecurse, "directory-recurse", false, "Recurse directory")
command.Flags().StringVar(&opts.configManagementPlugin, "config-management-plugin", "", "Config management plugin name")
command.Flags().StringArrayVar(&opts.jsonnetTlaStr, "jsonnet-tla-str", []string{}, "Jsonnet top level string arguments")
command.Flags().StringArrayVar(&opts.jsonnetTlaCode, "jsonnet-tla-code", []string{}, "Jsonnet top level code arguments")
command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d)")
command.Flags().StringVar(&opts.maintenanceWindows, "maintenance-windows", "", "Times during which auto-sync will be disabled, quote and separate values with commas: '0 11 * * *:1h,30 23 * * *:1h')")
}

// NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command
Expand Down Expand Up @@ -992,8 +1009,8 @@ func printApplicationTable(apps []argoappv1.Application, output *string) {
var fmtStr string
headers := []interface{}{"NAME", "CLUSTER", "NAMESPACE", "PROJECT", "STATUS", "HEALTH", "SYNCPOLICY", "CONDITIONS"}
if *output == "wide" {
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
headers = append(headers, "REPO", "PATH", "TARGET")
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
headers = append(headers, "REPO", "PATH", "TARGET", "MAINTENANCE")
} else {
fmtStr = "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n"
}
Expand All @@ -1010,7 +1027,7 @@ func printApplicationTable(apps []argoappv1.Application, output *string) {
formatConditionsSummary(app),
}
if *output == "wide" {
vals = append(vals, app.Spec.Source.RepoURL, app.Spec.Source.Path, app.Spec.Source.TargetRevision)
vals = append(vals, app.Spec.Source.RepoURL, app.Spec.Source.Path, app.Spec.Source.TargetRevision, app.Spec.SyncPolicy.Automated.ListMaintenanceWindows())
}
fmt.Fprintf(w, fmtStr, vals...)
}
Expand Down
13 changes: 11 additions & 2 deletions controller/appcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"time"

log "github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand Down Expand Up @@ -940,6 +940,14 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
logCtx.Infof("Skipping auto-sync: deletion in progress")
return nil
}
// Check if there are any active maintenance windows and skip if true
active, err := app.Spec.SyncPolicy.Automated.MaintenanceWindowActive()
if err != nil {
logCtx.Warnf("Error getting maintenance windows: %v", err)
}
if active {
return nil
}
// Only perform auto-sync if we detect OutOfSync status. This is to prevent us from attempting
// a sync when application is already in a Synced or Unknown state
if syncStatus.Status != appv1.SyncStatusCodeOutOfSync {
Expand Down Expand Up @@ -993,7 +1001,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
}

appIf := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace)
_, err := argo.SetAppOperation(appIf, app.Name, &op)
_, err = argo.SetAppOperation(appIf, app.Name, &op)
if err != nil {
logCtx.Errorf("Failed to initiate auto-sync to %s: %v", desiredCommitSHA, err)
return &appv1.ApplicationCondition{Type: appv1.ApplicationConditionSyncError, Message: err.Error()}
Expand Down Expand Up @@ -1117,3 +1125,4 @@ func toggledAutomatedSync(old *appv1.Application, new *appv1.Application) bool {
// nothing changed
return false
}

21 changes: 21 additions & 0 deletions controller/appcontroller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,27 @@ func TestSkipAutoSync(t *testing.T) {
assert.NoError(t, err)
assert.Nil(t, app.Operation)
}

// Verify we skip when there is an active maintenance window
{
app := newFakeApp()
sched := "* * * * *"
dur := "1m"
syncPol := &argoappv1.SyncPolicy{Automated: &argoappv1.SyncPolicyAutomated{}}
windows := []*argoappv1.MaintenanceWindow{{Schedule: &sched, Duration: &dur}}
syncPol.Automated.MaintenanceWindows = windows
app.Spec.SyncPolicy = syncPol
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
syncStatus := argoappv1.SyncStatus{
Status: argoappv1.SyncStatusCodeOutOfSync,
Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
}
cond := ctrl.autoSync(app, &syncStatus, []argoappv1.ResourceStatus{})
assert.Nil(t, cond)
app, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(test.FakeArgoCDNamespace).Get("my-app", metav1.GetOptions{})
assert.NoError(t, err)
assert.Nil(t, app.Operation)
}
}

// TestAutoSyncIndicateError verifies we skip auto-sync and return error condition if previous sync failed
Expand Down
24 changes: 24 additions & 0 deletions docs/user-guide/auto_sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ spec:
selfHeal: true
```

## Maintenance Windows

Maintenance windows are configurable windows of time where auto sync will not run. These are defined
by a start time in cron format and a duration. These windows do not affect the running of a manual
sync.


```bash
argocd app set --maintenance-windows '0 11 * * *:1h,30 23 * * *:1h'
```

Or by creating a maintenance windows list in the automated sync policy:

```yaml
spec:
syncPolicy:
automated:
maintenanceWindows:
- schedule: 0 11 * * *
duration: 1h
- schedule: 0 23 * * *
duration: 1h
```

## Automated Sync Semantics

* An automated sync will only be performed if the application is OutOfSync. Applications in a
Expand Down
14 changes: 14 additions & 0 deletions manifests/crds/application-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,20 @@ spec:
description: Automated will keep an application synced to the target
revision
properties:
maintenanceWindows:
description: 'MaintenanceWindows disables auto-syncing during
the configured time windows (default: false)'
items:
properties:
duration:
description: Duration is the amount of time the maintenance
window will be open
type: string
schedule:
description: Schedule is the time the window will begin
type: string
type: object
type: array
prune:
description: 'Prune will prune resources automatically as part
of automated sync (default: false)'
Expand Down
14 changes: 14 additions & 0 deletions manifests/ha/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,20 @@ spec:
description: Automated will keep an application synced to the target
revision
properties:
maintenanceWindows:
description: 'MaintenanceWindows disables auto-syncing during
the configured time windows (default: false)'
items:
properties:
duration:
description: Duration is the amount of time the maintenance
window will be open
type: string
schedule:
description: Schedule is the time the window will begin
type: string
type: object
type: array
prune:
description: 'Prune will prune resources automatically as part
of automated sync (default: false)'
Expand Down
14 changes: 14 additions & 0 deletions manifests/ha/namespace-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,20 @@ spec:
description: Automated will keep an application synced to the target
revision
properties:
maintenanceWindows:
description: 'MaintenanceWindows disables auto-syncing during
the configured time windows (default: false)'
items:
properties:
duration:
description: Duration is the amount of time the maintenance
window will be open
type: string
schedule:
description: Schedule is the time the window will begin
type: string
type: object
type: array
prune:
description: 'Prune will prune resources automatically as part
of automated sync (default: false)'
Expand Down
14 changes: 14 additions & 0 deletions manifests/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,20 @@ spec:
description: Automated will keep an application synced to the target
revision
properties:
maintenanceWindows:
description: 'MaintenanceWindows disables auto-syncing during
the configured time windows (default: false)'
items:
properties:
duration:
description: Duration is the amount of time the maintenance
window will be open
type: string
schedule:
description: Schedule is the time the window will begin
type: string
type: object
type: array
prune:
description: 'Prune will prune resources automatically as part
of automated sync (default: false)'
Expand Down
14 changes: 14 additions & 0 deletions manifests/namespace-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,20 @@ spec:
description: Automated will keep an application synced to the target
revision
properties:
maintenanceWindows:
description: 'MaintenanceWindows disables auto-syncing during
the configured time windows (default: false)'
items:
properties:
duration:
description: Duration is the amount of time the maintenance
window will be open
type: string
schedule:
description: Schedule is the time the window will begin
type: string
type: object
type: array
prune:
description: 'Prune will prune resources automatically as part
of automated sync (default: false)'
Expand Down
Loading