Skip to content

Commit

Permalink
fix(appset): prevent app deletion according to appset policy (argopro…
Browse files Browse the repository at this point in the history
…j#12172) (argoproj#15903)

* fix(applicationset): prevent app deletion according to appset policy

Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com>

* test: add unit test

Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com>

* fix: unit test

Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com>

* fix: remove TODO

Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com>

---------

Signed-off-by: mikutas <23391543+mikutas@users.noreply.github.com>
  • Loading branch information
mikutas authored Nov 10, 2023
1 parent 3d6568d commit 30ff2e5
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
34 changes: 31 additions & 3 deletions applicationset/controllers/applicationset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ func (r *ApplicationSetReconciler) Reconcile(ctx context.Context, req ctrl.Reque

// Do not attempt to further reconcile the ApplicationSet if it is being deleted.
if applicationSetInfo.ObjectMeta.DeletionTimestamp != nil {
if controllerutil.ContainsFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName) {
deleteAllowed := utils.DefaultPolicy(applicationSetInfo.Spec.SyncPolicy, r.Policy, r.EnablePolicyOverride).AllowDelete()
if !deleteAllowed {
if err := r.removeOwnerReferencesOnDeleteAppSet(ctx, applicationSetInfo); err != nil {
return ctrl.Result{}, err
}
controllerutil.RemoveFinalizer(&applicationSetInfo, argov1alpha1.ResourcesFinalizerName)
if err := r.Update(ctx, &applicationSetInfo); err != nil {
return ctrl.Result{}, err
}
}
}
return ctrl.Result{}, nil
}

Expand Down Expand Up @@ -731,10 +743,9 @@ func (r *ApplicationSetReconciler) createInCluster(ctx context.Context, logCtx *
return r.createOrUpdateInCluster(ctx, logCtx, applicationSet, createApps)
}

func (r *ApplicationSetReconciler) getCurrentApplications(_ context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
// TODO: Should this use the context param?
func (r *ApplicationSetReconciler) getCurrentApplications(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) ([]argov1alpha1.Application, error) {
var current argov1alpha1.ApplicationList
err := r.Client.List(context.Background(), &current, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace))
err := r.Client.List(ctx, &current, client.MatchingFields{".metadata.controller": applicationSet.Name}, client.InNamespace(applicationSet.Namespace))

if err != nil {
return nil, fmt.Errorf("error retrieving applications: %w", err)
Expand Down Expand Up @@ -875,6 +886,23 @@ func (r *ApplicationSetReconciler) removeFinalizerOnInvalidDestination(ctx conte
return nil
}

func (r *ApplicationSetReconciler) removeOwnerReferencesOnDeleteAppSet(ctx context.Context, applicationSet argov1alpha1.ApplicationSet) error {
applications, err := r.getCurrentApplications(ctx, applicationSet)
if err != nil {
return err
}

for _, app := range applications {
app.SetOwnerReferences([]metav1.OwnerReference{})
err := r.Client.Update(ctx, &app)
if err != nil {
return err
}
}

return nil
}

func (r *ApplicationSetReconciler) performProgressiveSyncs(ctx context.Context, logCtx *log.Entry, appset argov1alpha1.ApplicationSet, applications []argov1alpha1.Application, desiredApplications []argov1alpha1.Application, appMap map[string]argov1alpha1.Application) (map[string]bool, error) {

appDependencyList, appStepMap, err := r.buildAppDependencyList(logCtx, appset, desiredApplications)
Expand Down
75 changes: 75 additions & 0 deletions applicationset/controllers/applicationset_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,81 @@ func TestRemoveFinalizerOnInvalidDestination_DestinationTypes(t *testing.T) {
}
}

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

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

for _, c := range []struct {
// name is human-readable test name
name string
}{
{
name: "ownerReferences cleared",
},
} {
t.Run(c.name, func(t *testing.T) {
appSet := v1alpha1.ApplicationSet{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "namespace",
Finalizers: []string{v1alpha1.ResourcesFinalizerName},
},
Spec: v1alpha1.ApplicationSetSpec{
Template: v1alpha1.ApplicationSetTemplate{
Spec: v1alpha1.ApplicationSpec{
Project: "project",
},
},
},
}

app := v1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "app1",
Namespace: "namespace",
},
Spec: v1alpha1.ApplicationSpec{
Project: "project",
Source: &v1alpha1.ApplicationSource{Path: "path", TargetRevision: "revision", RepoURL: "repoURL"},
Destination: v1alpha1.ApplicationDestination{
Namespace: "namespace",
Server: "https://kubernetes.default.svc",
},
},
}

err := controllerutil.SetControllerReference(&appSet, &app, scheme)
assert.NoError(t, err, "Unexpected error")

initObjs := []crtclient.Object{&app, &appSet}

client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).WithIndex(&v1alpha1.Application{}, ".metadata.controller", appControllerIndexer).Build()

r := ApplicationSetReconciler{
Client: client,
Scheme: scheme,
Recorder: record.NewFakeRecorder(10),
KubeClientset: nil,
Cache: &fakeCache{},
}

err = r.removeOwnerReferencesOnDeleteAppSet(context.Background(), appSet)
assert.NoError(t, err, "Unexpected error")

retrievedApp := v1alpha1.Application{}
err = client.Get(context.Background(), crtclient.ObjectKeyFromObject(&app), &retrievedApp)
assert.NoError(t, err, "Unexpected error")

ownerReferencesRemoved := len(retrievedApp.OwnerReferences) == 0
assert.True(t, ownerReferencesRemoved)
})
}
}

func TestCreateApplications(t *testing.T) {

scheme := runtime.NewScheme()
Expand Down

0 comments on commit 30ff2e5

Please sign in to comment.