Skip to content

Commit

Permalink
feat: ApplicationSet add create-delete policy argoproj#9101 (argopr…
Browse files Browse the repository at this point in the history
…oj#11107)

* feat: ApplicationSet add create-delete policy

Signed-off-by: 久米 拓馬 <takuma.kume@pepabo.com>

* test for applicationSet policies

Signed-off-by: 久米 拓馬 <takuma.kume@pepabo.com>

* Update docs/operator-manual/applicationset/Controlling-Resource-Modification.md

Co-authored-by: Mubarak Jama <83465122+mubarak-j@users.noreply.github.com>
Signed-off-by: Takuma Kume <takuma.kume@gmail.com>

Signed-off-by: 久米 拓馬 <takuma.kume@pepabo.com>
Signed-off-by: Takuma Kume <takuma.kume@gmail.com>
Co-authored-by: Mubarak Jama <83465122+mubarak-j@users.noreply.github.com>
Signed-off-by: emirot <emirot.nolan@gmail.com>
  • Loading branch information
2 people authored and emirot committed Jan 27, 2023
1 parent 529bec7 commit ff7aa9b
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 4 deletions.
169 changes: 169 additions & 0 deletions applicationset/controllers/applicationset_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2028,3 +2028,172 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
})
}
}

func TestPolicies(t *testing.T) {
scheme := runtime.NewScheme()
err := argov1alpha1.AddToScheme(scheme)
assert.Nil(t, err)

err = argov1alpha1.AddToScheme(scheme)
assert.Nil(t, err)

defaultProject := argov1alpha1.AppProject{
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "argocd"},
Spec: argov1alpha1.AppProjectSpec{SourceRepos: []string{"*"}, Destinations: []argov1alpha1.ApplicationDestination{{Namespace: "*", Server: "https://kubernetes.default.svc"}}},
}
myCluster := argov1alpha1.Cluster{
Server: "https://kubernetes.default.svc",
Name: "my-cluster",
}

kubeclientset := kubefake.NewSimpleClientset()
argoDBMock := dbmocks.ArgoDB{}
argoDBMock.On("GetCluster", mock.Anything, "https://kubernetes.default.svc").Return(&myCluster, nil)
argoObjs := []runtime.Object{&defaultProject}

for _, c := range []struct {
name string
policyName string
allowedUpdate bool
allowedDelete bool
}{
{
name: "Apps are allowed to update and delete",
policyName: "sync",
allowedUpdate: true,
allowedDelete: true,
},
{
name: "Apps are not allowed to update and delete",
policyName: "create-only",
allowedUpdate: false,
allowedDelete: false,
},
{
name: "Apps are allowed to update, not allowed to delete",
policyName: "create-update",
allowedUpdate: true,
allowedDelete: false,
},
{
name: "Apps are allowed to delete, not allowed to update",
policyName: "create-delete",
allowedUpdate: false,
allowedDelete: true,
},
} {
t.Run(c.name, func(t *testing.T) {
policy := utils.Policies[c.policyName]
assert.NotNil(t, policy)

appSet := argov1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "argocd",
},
Spec: argov1alpha1.ApplicationSetSpec{
GoTemplate: true,
Generators: []argov1alpha1.ApplicationSetGenerator{
{
List: &argov1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{
{
Raw: []byte(`{"name": "my-app"}`),
},
},
},
},
},
Template: argov1alpha1.ApplicationSetTemplate{
ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{
Name: "{{.name}}",
Namespace: "argocd",
Annotations: map[string]string{
"key": "value",
},
},
Spec: argov1alpha1.ApplicationSpec{
Source: argov1alpha1.ApplicationSource{RepoURL: "https://github.com/argoproj/argocd-example-apps", Path: "guestbook"},
Project: "default",
Destination: argov1alpha1.ApplicationDestination{Server: "https://kubernetes.default.svc"},
},
},
},
}

client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appSet).Build()

r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Renderer: &utils.Render{},
Recorder: record.NewFakeRecorder(10),
Generators: map[string]generators.Generator{
"List": generators.NewListGenerator(),
},
ArgoDB: &argoDBMock,
ArgoAppClientset: appclientset.NewSimpleClientset(argoObjs...),
KubeClientset: kubeclientset,
Policy: policy,
}

req := ctrl.Request{
NamespacedName: types.NamespacedName{
Namespace: "argocd",
Name: "name",
},
}

// Check if Application is created
res, err := r.Reconcile(context.Background(), req)
assert.Nil(t, err)
assert.True(t, res.RequeueAfter == 0)

var app argov1alpha1.Application
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
assert.NoError(t, err)
assert.Equal(t, app.Annotations["key"], "value")

// Check if Application is updated
app.Annotations["key"] = "edited"
err = r.Client.Update(context.TODO(), &app)
assert.NoError(t, err)

res, err = r.Reconcile(context.Background(), req)
assert.Nil(t, err)
assert.True(t, res.RequeueAfter == 0)

err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
assert.NoError(t, err)

if c.allowedUpdate {
assert.Equal(t, app.Annotations["key"], "value")
} else {
assert.Equal(t, app.Annotations["key"], "edited")
}

// Check if Application is deleted
err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "name"}, &appSet)
assert.NoError(t, err)
appSet.Spec.Generators[0] = argov1alpha1.ApplicationSetGenerator{
List: &argov1alpha1.ListGenerator{
Elements: []apiextensionsv1.JSON{},
},
}
err = r.Client.Update(context.TODO(), &appSet)
assert.NoError(t, err)

res, err = r.Reconcile(context.Background(), req)
assert.Nil(t, err)
assert.True(t, res.RequeueAfter == 0)

err = r.Client.Get(context.TODO(), crtclient.ObjectKey{Namespace: "argocd", Name: "my-app"}, &app)
assert.NoError(t, err)
if c.allowedDelete {
assert.NotNil(t, app.DeletionTimestamp)
} else {
assert.Nil(t, app.DeletionTimestamp)
}
})
}
}
11 changes: 11 additions & 0 deletions applicationset/utils/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var Policies = map[string]Policy{
"sync": &SyncPolicy{},
"create-only": &CreateOnlyPolicy{},
"create-update": &CreateUpdatePolicy{},
"create-delete": &CreateDeletePolicy{},
}

type SyncPolicy struct{}
Expand Down Expand Up @@ -42,3 +43,13 @@ func (p *CreateOnlyPolicy) Update() bool {
func (p *CreateOnlyPolicy) Delete() bool {
return false
}

type CreateDeletePolicy struct{}

func (p *CreateDeletePolicy) Update() bool {
return false
}

func (p *CreateDeletePolicy) Delete() bool {
return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func NewCommand() *cobra.Command {

policyObj, exists := utils.Policies[policy]
if !exists {
log.Info("Policy value can be: sync, create-only, create-update")
log.Info("Policy value can be: sync, create-only, create-update, create-delete")
os.Exit(1)
}

Expand Down Expand Up @@ -200,7 +200,7 @@ func NewCommand() *cobra.Command {
"Enabling this will ensure there is only one active controller manager.")
command.Flags().StringVar(&namespace, "namespace", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACE", ""), "Argo CD repo namespace (default: argocd)")
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Argo CD repo server address")
command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", "sync"), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)")
command.Flags().StringVar(&policy, "policy", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_POLICY", "sync"), "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)")
command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ See 'How to modify ApplicationSet container parameters' below for detailed steps

The ApplicationSet controller supports a parameter `--policy`, which is specified on launch (within the controller Deployment container), and which restricts what types of modifications will be made to managed Argo CD `Application` resources.

The `--policy` parameter takes three values: `sync`, `create-only`, and `create-update`. (`sync` is the default, which is used if the `--policy` parameter is not specified; the other policies are described below).
The `--policy` parameter takes one of the following valid values: `sync`, `create-only`, `create-update`, and `create-delete`. (`sync` is the default, which is used if the `--policy` parameter is not specified; the other policies are described below).

To allow the ApplicationSet controller to *create* `Application` resources, but prevent any further modification, such as deletion, or modification of Application fields, add this parameter in the ApplicationSet controller:
```
Expand All @@ -34,6 +34,13 @@ To allow the ApplicationSet controller to create or modify `Application` resourc

This may be useful to users looking for additional protection against deletion of the Applications generated by the controller.

### Policy - `create-delete`: Prevent ApplicationSet controller from updating Applications

To allow the ApplicationSet controller to create or delete `Application` resources, but prevent Applications from being updated, add the following parameter to the ApplicationSet controller `Deployment`:
```
--policy create-delete
```

### Prevent an `Application`'s child resources from being deleted, when the parent Application is deleted

By default, when an `Application` resource is deleted by the ApplicationSet controller, all of the child resources of the Application will be deleted as well (such as, all of the Application's `Deployments`, `Services`, etc).
Expand Down
2 changes: 1 addition & 1 deletion docs/operator-manual/argocd-cmd-params-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ data:
applicationsetcontroller.enable.leader.election: "false"
# Argo CD repo namespace (default: argocd)
applicationsetcontroller.namespace: ""
# "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion)"
# "Modify how application is synced between the generator and the cluster. Default is 'sync' (create & update & delete), options: 'create-only', 'create-update' (no deletion), 'create-delete' (no update)"
applicationsetcontroller.policy: "sync"
# Print debug logs. Takes precedence over loglevel
applicationsetcontroller.debug: "false"
Expand Down

0 comments on commit ff7aa9b

Please sign in to comment.