Repository: kubermatic/kubermatic
Pull request #14616: Prevent System ApplicationDefinition deletion
Author: login: buraksekili
Labels: name: kind/bug
description: Categorizes issue or PR as related to a bug.
name: size/L
description: Denotes a PR that changes 100-499 lines, ignoring generated files.
name: lgtm
description: Indicates that a PR is ready to be merged.
name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: release-note-none
description: Denotes a PR that doesn't merit a release note.
name: dco-signoff: yes
description: Denotes that all commits in the pull request have the valid DCO signoff message.
name: sig/cluster-management
description: Denotes a PR or issue as being assigned to SIG Cluster Management.
name: sig/app-management
description: Denotes a PR or issue as being assigned to SIG App Management.
name: sig/networking
description: Denotes a PR or issue as being assigned to SIG Networking.
name: docs/none
description: Denotes a PR that doesn't need documentation (changes).
name: code-freeze-approved
description: Indicates a PR has been approved by release managers during code freeze.
Created: 2025-05-27T11:05:27Z | Merged: 2025-05-30T07:31:12Z

**What this PR does / why we need it**:
This PR 
- updates the webhooks of ApplicationDefinition to ensure that deleting a KKP managed ApplicationDefinition is rejected untill the label is deleted explicitly
- updates missing Cluster Autoscaler object name in reconciliation factory
- adds missing label to the ApplicationInstallations to allow UI to show them as SystemApplication
- adds `seccomp` to Cluster Autoscaler

**Which issue(s) this PR fixes**:
<!--optional, in `fixes #<issue number>` format, will close the issue(s) when PR gets merged-->
Fixes #14613

**What type of PR is this?**
<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind design

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
/kind chore
-->
/kind bug

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change? Then add your Release Note here**:
<!--
Write your release note. Release notes are being used to generate the changelog:
1. Enter your extended release note in the below block. If the PR requires additional action from users switching to the new release, include the string "action required".
2. If no release note is required, just write "NONE".
-->
```release-note
NONE
```

**Documentation**:
<!--
Please do one of the following options:
- Add a link to the existing documentation
- Add a link to the kubermatic/docs pull request
- If no documentation change is applicable then add:
  - TBD (documentation will be added later)
  - NONE (no documentation needed for this PR)
-->
```documentation
NONE
```


Linked issue #14613: Cluster autoscaler application installion is not labeled as a system app
### What happened?

<!-- Try to provide as much information as possible.
If you're reporting a security issue, please check the guidelines for reporting security issues:
https://github.com/kubermatic/kubermatic/blob/main/CONTRIBUTING.md#reporting-a-security-vulnerability -->

For cluster autoscaler system application the label `apps.kubermatic.k8c.io/managed-by=kkp` is missing and therefore it is not shown as a system application in kkp dashboard.
ref: #14588 

### Expected behavior

<!-- What did you expected to happen? -->

Cluster autoscaler application installation in an usercluster is shown as a system application in kkp dashboard.

### How to reproduce the issue?

<!-- Please provide as much information as possible, so we can reproduce the issue on our own. -->



### How is your environment configured?

- KKP version:
- Shared or separate master/seed clusters?:

### Provide your KKP manifest here (if applicable)

<!-- Providing an applicable manifest (KubermaticConfiguration, Seed, Cluster or other resources) will help us to reproduce the issue.
Please make sure to redact all secrets (e.g. passwords, URLs...)! -->

<details>

```yaml
# paste manifest here
```

</details>

### What cloud provider are you running on?

<!-- AWS, Azure, DigitalOcean, GCP, Hetzner Cloud, Nutanix, OpenStack, Equinix Metal (Packet), VMware vSphere, Other (e.g. baremetal or non-natively supported provider) -->



### What operating system are you running in your user cluster?

<!-- Ubuntu 20.04, Rocky Linux 8, Flatcar Linux, ... (optional, bug might not be related to user cluster) -->



### Additional information

<!-- Additional information about the bug you're reporting (optional). -->

Linked issue comments: user: login: soer3n
created_at: 2025-05-27T09:30:02Z
body: /milestone KKP 2.28
/label sig/app-management
author_association: CONTRIBUTOR
user: login: buraksekili
created_at: 2025-05-27T11:03:45Z
body: /assign
author_association: CONTRIBUTOR

Diff:
diff --git a/pkg/applicationdefinitions/application_catalog.go b/pkg/applicationdefinitions/application_catalog.go
index c60cc2a1232..26c667c6dfc 100644
--- a/pkg/applicationdefinitions/application_catalog.go
+++ b/pkg/applicationdefinitions/application_catalog.go
@@ -127,6 +127,7 @@ func systemApplicationDefinitionReconcilerFactory(
 				fileAppDef.Spec.DefaultValuesBlock = clusterAppDef.Spec.DefaultValuesBlock
 			}
 
+			clusterAppDef.Name = fileAppDef.Name
 			clusterAppDef.Spec = fileAppDef.Spec
 			return clusterAppDef, nil
 		}
diff --git a/pkg/applicationdefinitions/system-applications/cluster-autoscaler.yaml b/pkg/applicationdefinitions/system-applications/cluster-autoscaler.yaml
index bbeb313f7d0..689b86bba55 100644
--- a/pkg/applicationdefinitions/system-applications/cluster-autoscaler.yaml
+++ b/pkg/applicationdefinitions/system-applications/cluster-autoscaler.yaml
@@ -38,6 +38,9 @@ spec:
     clusterAPIMode: incluster-incluster
     autoDiscovery:
       namespace: kube-system
+    securityContext:
+      seccompProfile:
+        type: RuntimeDefault
     image:
       # `Cluster.AutoscalerVersion` is injected by KKP based on the Kubernetes version of the cluster.
       tag: '{{ .Cluster.AutoscalerVersion }}'
diff --git a/pkg/controller/seed-controller-manager/cni-application-installation-controller/controller.go b/pkg/controller/seed-controller-manager/cni-application-installation-controller/controller.go
index b5918e34d60..42a30602b21 100644
--- a/pkg/controller/seed-controller-manager/cni-application-installation-controller/controller.go
+++ b/pkg/controller/seed-controller-manager/cni-application-installation-controller/controller.go
@@ -336,7 +336,7 @@ func ApplicationInstallationReconciler(cluster *kubermaticv1.Cluster, overwriteR
 			}
 			app.Spec.DeployOptions = &appskubermaticv1.DeployOptions{
 				Helm: &appskubermaticv1.HelmDeployOptions{
-					// Use atomic deployment, as atomic (with fixed retries count) migitates breaking the etcd due to creating events
+					// Use atomic deployment, as atomic (with fixed retries count) mitigates breaking the etcd due to creating events
 					// when retrying on failure without a limit
 					Atomic: true,
 					Wait:   true,
diff --git a/pkg/controller/seed-controller-manager/default-application-controller/controller.go b/pkg/controller/seed-controller-manager/default-application-controller/controller.go
index b490ccd9dea..f1355acb840 100644
--- a/pkg/controller/seed-controller-manager/default-application-controller/controller.go
+++ b/pkg/controller/seed-controller-manager/default-application-controller/controller.go
@@ -210,7 +210,7 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *kubermaticv1.Cluste
 	var errors []error
 	for _, application := range applications {
 		// Using reconciler framework here doesn't help since the namespaces are different for the application installations.
-		err := r.ensureApplicationInstallation(ctx, userClusterClient, application, cluster)
+		err := r.ensureApplicationInstallation(ctx, userClusterClient, application)
 		if err != nil {
 			errors = append(errors, err)
 		}
@@ -234,7 +234,7 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *kubermaticv1.Cluste
 	return nil, kerrors.NewAggregate(errors)
 }
 
-func (r *Reconciler) ensureApplicationInstallation(ctx context.Context, userClusterClient ctrlruntimeclient.Client, application appskubermaticv1.ApplicationDefinition, cluster *kubermaticv1.Cluster) error {
+func (r *Reconciler) ensureApplicationInstallation(ctx context.Context, userClusterClient ctrlruntimeclient.Client, application appskubermaticv1.ApplicationDefinition) error {
 	// First check if the installation is already present to avoid to deploy an application twice in different namespaces by mistake
 	// for this we need to list all existing applications installations
 	existingApplicationList := &appskubermaticv1.ApplicationInstallationList{}
@@ -281,15 +281,19 @@ func (r *Reconciler) ensureApplicationInstallation(ctx context.Context, userClus
 	}
 
 	reconcilers := []reconciling.NamedApplicationInstallationReconcilerFactory{
-		ApplicationInstallationReconciler(ctx, r.log, application, currentApplicationInstallation, cluster),
+		ApplicationInstallationReconciler(r.log, application),
 	}
+
 	return reconciling.ReconcileApplicationInstallations(ctx, reconcilers, namespaceName, userClusterClient)
 }
 
-func ApplicationInstallationReconciler(ctx context.Context, logger *zap.SugaredLogger, application appskubermaticv1.ApplicationDefinition, exisitingApplication *appskubermaticv1.ApplicationInstallation, cluster *kubermaticv1.Cluster) reconciling.NamedApplicationInstallationReconcilerFactory {
+func ApplicationInstallationReconciler(
+	logger *zap.SugaredLogger,
+	application appskubermaticv1.ApplicationDefinition,
+) reconciling.NamedApplicationInstallationReconcilerFactory {
 	return func() (string, reconciling.ApplicationInstallationReconciler) {
 		applicationName := application.Name
-		// generatedApplication, err := generateApplicationInstallation(ctx, application, namespace)
+
 		return applicationName, func(app *appskubermaticv1.ApplicationInstallation) (*appskubermaticv1.ApplicationInstallation, error) {
 			appVersion := application.Spec.DefaultVersion
 			if appVersion == "" {
@@ -316,6 +320,7 @@ func ApplicationInstallationReconciler(ctx context.Context, logger *zap.SugaredL
 					}
 				}
 			}
+
 			err := convertDefaultValuesToDefaultValuesBlock(&application)
 			if err != nil {
 				// This is a non-critical error and we can still continue by using the `values` field instead of the `valuesBlock` field.
diff --git a/pkg/controller/seed-controller-manager/initial-application-installation-controller/controller.go b/pkg/controller/seed-controller-manager/initial-application-installation-controller/controller.go
index 75d377cf2c6..554d0f234d6 100644
--- a/pkg/controller/seed-controller-manager/initial-application-installation-controller/controller.go
+++ b/pkg/controller/seed-controller-manager/initial-application-installation-controller/controller.go
@@ -38,6 +38,7 @@ import (
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/tools/record"
 	"sigs.k8s.io/controller-runtime/pkg/builder"
 	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -183,7 +184,12 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *kubermaticv1.Cluste
 	return nil, nil
 }
 
-func (r *Reconciler) createInitialApplicationInstallation(ctx context.Context, client ctrlruntimeclient.Client, application apiv1.Application, cluster *kubermaticv1.Cluster) error {
+func (r *Reconciler) createInitialApplicationInstallation(
+	ctx context.Context,
+	client ctrlruntimeclient.Client,
+	application apiv1.Application,
+	cluster *kubermaticv1.Cluster,
+) error {
 	namespace := application.Namespace
 	if namespace == "" {
 		namespace = application.Spec.Namespace.Name
@@ -208,12 +214,20 @@ func (r *Reconciler) createInitialApplicationInstallation(ctx context.Context, c
 		}
 	}
 
+	applicationDefinition := appskubermaticv1.ApplicationDefinition{}
+
+	err = r.Get(ctx, types.NamespacedName{Name: application.Spec.ApplicationRef.Name}, &applicationDefinition)
+	if err != nil && !apierrors.IsNotFound(err) {
+		return fmt.Errorf("failed to get ApplicationDefinition %s: %w", application.Spec.ApplicationRef.Name, err)
+	}
+
 	// Create the application installation resource.
 	applicationInstallation := appskubermaticv1.ApplicationInstallation{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:        application.Name,
 			Namespace:   namespace,
 			Annotations: application.Annotations,
+			Labels:      applicationDefinition.Labels,
 		},
 		Spec: appskubermaticv1.ApplicationInstallationSpec{
 			Namespace: &appskubermaticv1.AppNamespaceSpec{
diff --git a/pkg/validation/application_definition.go b/pkg/validation/application_definition.go
index 19546b1526c..eaa453e9ab9 100644
--- a/pkg/validation/application_definition.go
+++ b/pkg/validation/application_definition.go
@@ -58,6 +58,35 @@ func ValidateApplicationDefinitionUpdate(newAd appskubermaticv1.ApplicationDefin
 	return allErrs
 }
 
+func deleteSystemAppErrorMsg() string {
+	return fmt.Sprintf("ApplicationDefinition for system applications are managed by KKP and should not be deleted. "+
+		"Deletion can impact user clusters where these Applications are installed. "+
+		"If you would still like to remove the ApplicationDefinition, remove the %q label and then delete it.",
+		appskubermaticv1.ApplicationManagedByLabel,
+	)
+}
+
+func ValidateApplicationDefinitionDelete(ad appskubermaticv1.ApplicationDefinition) field.ErrorList {
+	labels := ad.GetLabels()
+	if labels != nil {
+		if val := labels[appskubermaticv1.ApplicationManagedByLabel]; val == appskubermaticv1.ApplicationManagedByKKPValue {
+			return field.ErrorList{
+				field.Invalid(
+					field.NewPath("metadata").Child("labels"),
+					labels,
+					deleteSystemAppErrorMsg(),
+				),
+			}
+		}
+	}
+
+	var parentFieldPath *field.Path = nil
+	allErrs := field.ErrorList{}
+
+	allErrs = append(allErrs, ValidateApplicationDefinitionWithOpenAPI(ad, parentFieldPath)...)
+	return allErrs
+}
+
 func ValidateApplicationVersions(vs []appskubermaticv1.ApplicationVersion, parentFieldPath *field.Path) field.ErrorList {
 	allErrs := field.ErrorList{}
 
diff --git a/pkg/validation/application_definition_test.go b/pkg/validation/application_definition_test.go
index a716ddf8677..17bf625208c 100644
--- a/pkg/validation/application_definition_test.go
+++ b/pkg/validation/application_definition_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package validation
 
 import (
+	"fmt"
 	"testing"
 
 	appskubermaticv1 "k8c.io/kubermatic/sdk/v2/apis/apps.kubermatic/v1"
@@ -1144,3 +1145,39 @@ func TestValidateApplicationDefinitionUpdate(t *testing.T) {
 		})
 	}
 }
+
+func TestValidateApplicationDefinitionDelete(t *testing.T) {
+	notManagedAppName := "not-managed"
+	managedAppName := "managed"
+	appDef := getApplicationDefinition(notManagedAppName, false, false, nil, nil)
+	managedAppDef := getApplicationDefinition(managedAppName, false, false, nil, map[string]string{
+		appskubermaticv1.ApplicationManagedByLabel: appskubermaticv1.ApplicationManagedByKKPValue,
+	})
+
+	testCases := []struct {
+		name          string
+		ad            *appskubermaticv1.ApplicationDefinition
+		expectedError string
+	}{
+		{
+			name:          "scenario 1: application deletion is allowed for non-managed application definition",
+			ad:            appDef,
+			expectedError: "[]",
+		},
+		{
+			name:          "scenario 2: application deletion is not allowed for managed application definition",
+			ad:            managedAppDef,
+			expectedError: `[metadata.labels: Invalid value: map[string]string{"apps.kubermatic.k8c.io/managed-by":"kkp"}: ` + deleteSystemAppErrorMsg() + `]`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.name, func(t *testing.T) {
+			err := ValidateApplicationDefinitionDelete(*testCase.ad)
+
+			if fmt.Sprint(err) != testCase.expectedError {
+				t.Fatalf("expected error to be %s but got %v", testCase.expectedError, err)
+			}
+		})
+	}
+}
diff --git a/pkg/validation/application_installation_test.go b/pkg/validation/application_installation_test.go
index fc14dbd96d6..2efe19b5c21 100644
--- a/pkg/validation/application_installation_test.go
+++ b/pkg/validation/application_installation_test.go
@@ -40,7 +40,7 @@ const (
 
 // TestValidateApplicationInstallationSpec tests the validation for ApplicationInstallation creation.
 func TestValidateApplicationInstallationSpec(t *testing.T) {
-	ad := getApplicationDefinition(defaultAppName, false, false, nil)
+	ad := getApplicationDefinition(defaultAppName, false, false, nil, nil)
 	fakeClient := fake.
 		NewClientBuilder().
 		WithObjects(ad).
@@ -227,8 +227,8 @@ func TestValidateApplicationInstallationSpec(t *testing.T) {
 
 // TestValidateApplicationInstallationSpec tests the validation for ApplicationInstallation creation.
 func TestValidateApplicationInstallationUpdate(t *testing.T) {
-	ad := getApplicationDefinition(defaultAppName, false, false, nil)
-	updatedAD := getApplicationDefinition("updated-app", false, false, nil)
+	ad := getApplicationDefinition(defaultAppName, false, false, nil, nil)
+	updatedAD := getApplicationDefinition("updated-app", false, false, nil, nil)
 	fakeClient := fake.
 		NewClientBuilder().
 		WithObjects(ad, updatedAD).
@@ -417,11 +417,11 @@ func TestValidateApplicationInstallationDelete(t *testing.T) {
 	clusterName := "cluster-1"
 
 	applicationDefinitions := []ctrlruntimeclient.Object{
-		getApplicationDefinition(appName, false, false, nil),
-		getApplicationDefinition(defaultAppName, true, false, nil),
-		getApplicationDefinition(enforcedAppName, false, true, nil),
-		getApplicationDefinition(enforcedAppDCName, false, true, []string{dcName}),
-		getApplicationDefinition(enforcedWrongDCName, false, true, []string{"dc-2"}),
+		getApplicationDefinition(appName, false, false, nil, nil),
+		getApplicationDefinition(defaultAppName, true, false, nil, nil),
+		getApplicationDefinition(enforcedAppName, false, true, nil, nil),
+		getApplicationDefinition(enforcedAppDCName, false, true, []string{dcName}, nil),
+		getApplicationDefinition(enforcedWrongDCName, false, true, []string{"dc-2"}, nil),
 	}
 
 	fakeClient := fake.
@@ -489,19 +489,43 @@ func TestValidateApplicationInstallationDelete(t *testing.T) {
 	}
 }
 
-func getApplicationDefinition(name string, defaulted bool, enforced bool, datacenters []string) *appskubermaticv1.ApplicationDefinition {
+func getApplicationDefinition(name string, defaulted, enforced bool, datacenters []string, labels map[string]string) *appskubermaticv1.ApplicationDefinition {
 	return &appskubermaticv1.ApplicationDefinition{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "ApplicationDefinition",
+			APIVersion: "apps.kubermatic.k8c.io/v1",
+		},
 		ObjectMeta: metav1.ObjectMeta{
-			Name: name,
+			Name:   name,
+			Labels: labels,
 		},
 		Spec: appskubermaticv1.ApplicationDefinitionSpec{
 			Description: "Description",
+			Method:      appskubermaticv1.HelmTemplateMethod,
 			Versions: []appskubermaticv1.ApplicationVersion{
 				{
 					Version: defaultAppVersion,
+					Template: appskubermaticv1.ApplicationTemplate{
+						Source: appskubermaticv1.ApplicationSource{
+							Helm: &appskubermaticv1.HelmSource{
+								URL:          "http://example.com/charts",
+								ChartName:    "test-chart",
+								ChartVersion: defaultAppVersion,
+							},
+						},
+					},
 				},
 				{
 					Version: defaultAppSecondaryVersion,
+					Template: appskubermaticv1.ApplicationTemplate{
+						Source: appskubermaticv1.ApplicationSource{
+							Helm: &appskubermaticv1.HelmSource{
+								URL:          "http://example.com/charts",
+								ChartName:    "test-chart",
+								ChartVersion: defaultAppSecondaryVersion,
+							},
+						},
+					},
 				},
 			},
 			Enforced: enforced,
diff --git a/pkg/webhook/application/applicationdefinition/validation/validation.go b/pkg/webhook/application/applicationdefinition/validation/validation.go
index a6aff0a819a..e2516ed326b 100644
--- a/pkg/webhook/application/applicationdefinition/validation/validation.go
+++ b/pkg/webhook/application/applicationdefinition/validation/validation.go
@@ -74,8 +74,11 @@ func (h *AdmissionHandler) Handle(ctx context.Context, req webhook.AdmissionRequ
 		allErrs = append(allErrs, validation.ValidateApplicationDefinitionUpdate(*ad, *oldAD)...)
 
 	case admissionv1.Delete:
-		// NOP we always allow delete operations
+		if err := h.decoder.DecodeRaw(req.OldObject, oldAD); err != nil {
+			return webhook.Errored(http.StatusBadRequest, err)
+		}
 
+		allErrs = append(allErrs, validation.ValidateApplicationDefinitionDelete(*oldAD)...)
 	default:
 		return webhook.Errored(http.StatusBadRequest, fmt.Errorf("%s not supported on ApplicationdDefinition resources", req.Operation))
 	}


Reviews:
user: login: ahmedwaleedmalik
body: So from our conversation in the SIG I thought there is somehow some issue with the system apps and they are not working with defaulting/enforcement. But turns out you are trying to do something that's not required IMO.

> adds a new configuration parameter for System applications to configure reconciliation interval (by default 1h)

It's fine to add it only for System Applications but maybe having a `DefaultReconciliationInterval` in ApplicationDefinitions will be more helpful. But please keep in mind that this doesn't fix https://github.com/kubermatic/kubermatic/issues/14611 in anyway(I know the ticket is not linked here but just saying).

> updates missing metadata in reconciliation factory

The missing metadata is still not fixed in this PR. This doesn't address https://github.com/kubermatic/kubermatic/issues/14613

> installs the system applicationdefinition by default

That's definitely not what we want to do.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-27T20:35:17Z
user: login: ahmedwaleedmalik
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T12:08:47Z
user: login: buraksekili
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T13:14:49Z
user: login: buraksekili
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T13:16:07Z
user: login: ahmedwaleedmalik
body: /approve

/hold since you mentioned that you are testing it right now. If everything works well, please feel free to remove the hold.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-29T13:28:30Z
user: login: cnvergence
body: /approve
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-29T15:54:00Z
user: login: ahmedwaleedmalik
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-30T07:30:57Z

Review comments:
diff_hunk: @@ -194,6 +196,12 @@ func (r *Reconciler) reconcile(ctx context.Context, cluster *kubermaticv1.Cluste
 			continue
 		}
 
+		if managedByKKP := applicationDefinition.Labels[appskubermaticv1.ApplicationManagedByLabel]; managedByKKP == appskubermaticv1.ApplicationManagedByKKPValue {
+			if appType := applicationDefinition.Labels[appskubermaticv1.ApplicationTypeLabel]; appType != appskubermaticv1.ApplicationTypeCNIValue {
+				applications = append(applications, applicationDefinition)
+			}
+		}
path: pkg/controller/seed-controller-manager/default-application-controller/controller.go
user: login: ahmedwaleedmalik
body: Can you please explain a bit what we are trying to do here? IMO this is not required.
created_at: 2025-05-27T20:19:26Z
author_association: MEMBER
diff_hunk: @@ -355,6 +380,13 @@ func ApplicationInstallationReconciler(ctx context.Context, logger *zap.SugaredL
 				app.Spec.Values = *application.Spec.DefaultValues
 			}
 
+			// add reconciliation interval to the system apps except Cilium, as it is managed by its own controller.
+			if app.Labels[appskubermaticv1.ApplicationManagedByLabel] == appskubermaticv1.ApplicationManagedByKKPValue {
+				if app.Labels[appskubermaticv1.ApplicationTypeLabel] != appskubermaticv1.ApplicationTypeCNIValue {
+					app.Spec.ReconciliationInterval = reconciliationInterval
+				}
+			}
path: pkg/controller/seed-controller-manager/default-application-controller/controller.go
user: login: ahmedwaleedmalik
body: Why do we need a reconciliation interval here? This will only work for cluster-autoscaler application when it's either being defaulted or enforced. This is not how this application is installed most of the time. 
created_at: 2025-05-27T20:26:04Z
author_association: MEMBER
diff_hunk: @@ -355,6 +380,13 @@ func ApplicationInstallationReconciler(ctx context.Context, logger *zap.SugaredL
 				app.Spec.Values = *application.Spec.DefaultValues
 			}
 
+			// add reconciliation interval to the system apps except Cilium, as it is managed by its own controller.
+			if app.Labels[appskubermaticv1.ApplicationManagedByLabel] == appskubermaticv1.ApplicationManagedByKKPValue {
+				if app.Labels[appskubermaticv1.ApplicationTypeLabel] != appskubermaticv1.ApplicationTypeCNIValue {
+					app.Spec.ReconciliationInterval = reconciliationInterval
+				}
+			}
path: pkg/controller/seed-controller-manager/default-application-controller/controller.go
user: login: ahmedwaleedmalik
body: If you want to set `ReconciliationInterval` for some apps in general i'd rather suggest adding `DefaultReconciliationInterval` in ApplicationDefinition and then if the value is configured(not empty/null) use it in the ApplicationInstallation,
created_at: 2025-05-27T20:27:42Z
author_association: MEMBER
diff_hunk: @@ -38,6 +38,9 @@ spec:
     clusterAPIMode: incluster-incluster
     autoDiscovery:
       namespace: kube-system
+    securityContext:
+      seccompProfile:
+        type: RuntimeDefault
path: pkg/applicationdefinitions/system-applications/cluster-autoscaler.yaml
user: login: ahmedwaleedmalik
body: 👍 
created_at: 2025-05-27T20:28:46Z
author_association: MEMBER
diff_hunk: @@ -58,6 +58,27 @@ func ValidateApplicationDefinitionUpdate(newAd appskubermaticv1.ApplicationDefin
 	return allErrs
 }
 
+func ValidateApplicationDefinitionDelete(ad appskubermaticv1.ApplicationDefinition) field.ErrorList {
+	labels := ad.GetLabels()
+	if labels != nil {
+		if _, exists := labels[appskubermaticv1.ApplicationManagedByLabel]; exists {
path: pkg/validation/application_definition.go
user: login: ahmedwaleedmalik
body: I think you should also check the value of the label and make sure it's `appskubermaticv1.ApplicationManagedByKKPValue` i.e. "kkp"
created_at: 2025-05-29T12:04:11Z
author_association: MEMBER
diff_hunk: @@ -185,6 +185,19 @@ func ValidateApplicationInstallationDelete(ctx context.Context, client ctrlrunti
 		return allErrs
 	}
 
+	labels := ai.GetLabels()
+	if labels != nil {
+		if _, exists := labels[appskubermaticv1.ApplicationManagedByLabel]; exists {
+			return field.ErrorList{
+				field.Invalid(
+					field.NewPath("metadata").Child("labels"),
+					labels,
+					fmt.Sprintf("Managed ApplicationInstallation cannot be deleted. To delete it, remove the %q label", appskubermaticv1.ApplicationManagedByLabel),
+				),
+			}
+		}
+	}
path: pkg/validation/application_installation.go
user: login: ahmedwaleedmalik
body: This can be really problematic because this would disallow cilium `ApplicationInstallation` from being deleted when a cluster itself is marked for deletion. I would say don't do this. 

If someone really wants to block deletion for ApplicationInstallations they can always enforce an application.
created_at: 2025-05-29T12:06:16Z
author_association: MEMBER
diff_hunk: @@ -58,6 +58,27 @@ func ValidateApplicationDefinitionUpdate(newAd appskubermaticv1.ApplicationDefin
 	return allErrs
 }
 
+func ValidateApplicationDefinitionDelete(ad appskubermaticv1.ApplicationDefinition) field.ErrorList {
+	labels := ad.GetLabels()
+	if labels != nil {
+		if _, exists := labels[appskubermaticv1.ApplicationManagedByLabel]; exists {
+			return field.ErrorList{
+				field.Invalid(
+					field.NewPath("metadata").Child("labels"),
+					labels,
+					fmt.Sprintf("Managed ApplicationDefinition cannot be deleted. To delete it, remove the %q label", appskubermaticv1.ApplicationManagedByLabel),
path: pkg/validation/application_definition.go
user: login: ahmedwaleedmalik
body: ```suggestion
					fmt.Sprintf("ApplicationDefinition for system applications are managed by KKP and should not be deleted. Deletion can impact user clusters where these Applications are installed. If you would still like to remove the ApplicationDefinition, remove the %q label and then delete it.", appskubermaticv1.ApplicationManagedByLabel),
```

Something along these lines might be better suited but i'd leave the final message up to you.
created_at: 2025-05-29T12:08:41Z
author_association: MEMBER
diff_hunk: @@ -185,6 +185,19 @@ func ValidateApplicationInstallationDelete(ctx context.Context, client ctrlrunti
 		return allErrs
 	}
 
+	labels := ai.GetLabels()
+	if labels != nil {
+		if _, exists := labels[appskubermaticv1.ApplicationManagedByLabel]; exists {
+			return field.ErrorList{
+				field.Invalid(
+					field.NewPath("metadata").Child("labels"),
+					labels,
+					fmt.Sprintf("Managed ApplicationInstallation cannot be deleted. To delete it, remove the %q label", appskubermaticv1.ApplicationManagedByLabel),
+				),
+			}
+		}
+	}
path: pkg/validation/application_installation.go
user: login: buraksekili
body: Thanks for the details! I reverted this change.
created_at: 2025-05-29T13:14:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -355,6 +380,13 @@ func ApplicationInstallationReconciler(ctx context.Context, logger *zap.SugaredL
 				app.Spec.Values = *application.Spec.DefaultValues
 			}
 
+			// add reconciliation interval to the system apps except Cilium, as it is managed by its own controller.
+			if app.Labels[appskubermaticv1.ApplicationManagedByLabel] == appskubermaticv1.ApplicationManagedByKKPValue {
+				if app.Labels[appskubermaticv1.ApplicationTypeLabel] != appskubermaticv1.ApplicationTypeCNIValue {
+					app.Spec.ReconciliationInterval = reconciliationInterval
+				}
+			}
path: pkg/controller/seed-controller-manager/default-application-controller/controller.go
user: login: buraksekili
body: for reference, i reverted this enhancement
created_at: 2025-05-29T13:16:07Z
author_association: CONTRIBUTOR

PR comments:
user: login: buraksekili
created_at: 2025-05-27T16:58:21Z
body: /assign
/cc @ahmedwaleedmalik 
author_association: CONTRIBUTOR
user: login: mohamed-rafraf
created_at: 2025-05-27T17:08:04Z
body: /milestone KKP 2.28
author_association: MEMBER
user: login: kubermatic-bot
created_at: 2025-05-29T13:28:34Z
body: LGTM label has been added.  <details>Git tree hash: 092c5a64b54f93c45c6b400187f058abab0089b7</details>
author_association: CONTRIBUTOR
user: login: buraksekili
created_at: 2025-05-29T13:29:21Z
body: /hold until tests are done
author_association: CONTRIBUTOR
user: login: kubermatic-bot
created_at: 2025-05-29T15:54:09Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubermatic/kubermatic/pull/14616#pullrequestreview-2878282157" title="Approved">ahmedwaleedmalik</a>*, *<a href="https://github.com/kubermatic/kubermatic/pull/14616#pullrequestreview-2878790606" title="Approved">cnvergence</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubermatic%2Fkubermatic).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubermatic/kubermatic/blob/main/OWNERS)~~ [ahmedwaleedmalik,cnvergence]
- ~~[pkg/applicationdefinitions/OWNERS](https://github.com/kubermatic/kubermatic/blob/main/pkg/applicationdefinitions/OWNERS)~~ [ahmedwaleedmalik,cnvergence]
- ~~[pkg/controller/OWNERS](https://github.com/kubermatic/kubermatic/blob/main/pkg/controller/OWNERS)~~ [ahmedwaleedmalik,cnvergence]
- ~~[pkg/controller/seed-controller-manager/cni-application-installation-controller/OWNERS](https://github.com/kubermatic/kubermatic/blob/main/pkg/controller/seed-controller-manager/cni-application-installation-controller/OWNERS)~~ [cnvergence]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: buraksekili
created_at: 2025-05-29T22:46:48Z
body: /unhold


author_association: CONTRIBUTOR
user: login: kubermatic-bot
created_at: 2025-05-30T07:31:01Z
body: LGTM label has been added.  <details>Git tree hash: 787158ea55b11d7b2ec8f78523c2a3068f521afc</details>
author_association: CONTRIBUTOR