diff --git a/controller/state.go b/controller/state.go index decd7218017dd..d9a0ed4f05acd 100644 --- a/controller/state.go +++ b/controller/state.go @@ -702,10 +702,11 @@ func (m *appStateManager) isSelfReferencedObj(obj *unstructured.Unstructured, ap // In order for us to assume obj to be managed by this application, the // values from the annotation have to match the properties from the live - // object. + // object. Cluster scoped objects carry the app's destination namespace + // in the tracking annotation, but are unique in GVK + name combination. appInstance := m.resourceTracking.GetAppInstance(obj, appLabelKey, trackingMethod) if appInstance != nil { - return obj.GetNamespace() == appInstance.Namespace && + return (obj.GetNamespace() == appInstance.Namespace || obj.GetNamespace() == "") && obj.GetName() == appInstance.Name && obj.GetObjectKind().GroupVersionKind().Group == appInstance.Group && obj.GetObjectKind().GroupVersionKind().Kind == appInstance.Kind diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index a2983093dd74a..a5d39c6734f6e 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" + rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -1671,9 +1672,11 @@ func TestSyncWithInfos(t *testing.T) { }) } -//Given: argocd app create does not provide --dest-namespace -// Manifest contains resource console which does not require namespace -//Expect: no app.Status.Conditions +// Given: argocd app create does not provide --dest-namespace +// +// Manifest contains resource console which does not require namespace +// +// Expect: no app.Status.Conditions func TestCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) { Given(t). Path(globalWithNoNameSpace). @@ -1688,10 +1691,12 @@ func TestCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) { }) } -//Given: argocd app create does not provide --dest-namespace -// Manifest contains resource deployment, and service which requires namespace -// Deployment and service do not have namespace in manifest -//Expect: app.Status.Conditions for deployment ans service which does not have namespace in manifest +// Given: argocd app create does not provide --dest-namespace +// +// Manifest contains resource deployment, and service which requires namespace +// Deployment and service do not have namespace in manifest +// +// Expect: app.Status.Conditions for deployment ans service which does not have namespace in manifest func TestCreateAppWithNoNameSpaceWhenRequired(t *testing.T) { Given(t). Path(guestbookPath). @@ -1709,11 +1714,13 @@ func TestCreateAppWithNoNameSpaceWhenRequired(t *testing.T) { }) } -//Given: argocd app create does not provide --dest-namespace -// Manifest contains resource deployment, and service which requires namespace -// Some deployment and service has namespace in manifest -// Some deployment and service does not have namespace in manifest -//Expect: app.Status.Conditions for deployment and service which does not have namespace in manifest +// Given: argocd app create does not provide --dest-namespace +// +// Manifest contains resource deployment, and service which requires namespace +// Some deployment and service has namespace in manifest +// Some deployment and service does not have namespace in manifest +// +// Expect: app.Status.Conditions for deployment and service which does not have namespace in manifest func TestCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) { Given(t). Path(guestbookWithNamespace). @@ -1789,10 +1796,13 @@ func TestListResource(t *testing.T) { } // Given application is set with --sync-option CreateNamespace=true -// application --dest-namespace does not exist +// +// application --dest-namespace does not exist +// // Verity application --dest-namespace is created -// application sync successful -// when application is deleted, --dest-namespace is not deleted +// +// application sync successful +// when application is deleted, --dest-namespace is not deleted func TestNamespaceAutoCreation(t *testing.T) { SkipOnEnv(t, "OPENSHIFT") updatedNamespace := getNewNamespace(t) @@ -2392,5 +2402,58 @@ func TestAnnotationTrackingExtraResources(t *testing.T) { Then(). Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + And(func() { + // Add a cluster-scoped resource that is not referencing itself + FailOnErr(KubeClientset.RbacV1().ClusterRoles().Create(context.Background(), &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "e2e-test-clusterrole", + Annotations: map[string]string{ + common.AnnotationKeyAppInstance: fmt.Sprintf("%s:rbac.authorization.k8s.io/ClusterRole:%s/e2e-other-clusterrole", Name(), DeploymentNamespace()), + }, + Labels: map[string]string{ + fixture.TestingLabel: "true", + }, + }, + }, metav1.CreateOptions{})) + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + And(func() { + // Add a cluster-scoped resource that is referencing itself + FailOnErr(KubeClientset.RbacV1().ClusterRoles().Create(context.Background(), &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "e2e-other-clusterrole", + Annotations: map[string]string{ + common.AnnotationKeyAppInstance: fmt.Sprintf("%s:rbac.authorization.k8s.io/ClusterRole:%s/e2e-other-clusterrole", Name(), DeploymentNamespace()), + }, + Labels: map[string]string{ + fixture.TestingLabel: "true", + }, + }, + }, metav1.CreateOptions{})) + }). + Refresh(RefreshTypeNormal). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(HealthIs(health.HealthStatusHealthy)). + When(). + Sync("--prune"). + And(func() { + // The extra configmap must be pruned now, because it's tracked and does not exist in git + cr, err := KubeClientset.RbacV1().ClusterRoles().Get(context.Background(), "e2e-other-clusterrole", metav1.GetOptions{}) + require.Error(t, err) + require.Equal(t, "", cr.Name) + }). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(health.HealthStatusHealthy)) + } diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 7b376d436d79d..4a9f6b3d6c00f 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -41,7 +41,7 @@ const ( defaultAdminPassword = "password" defaultAdminUsername = "admin" DefaultTestUserPassword = "password" - testingLabel = "e2e.argoproj.io" + TestingLabel = "e2e.argoproj.io" ArgoCDNamespace = "argocd-e2e" ArgoCDAppNamespace = "argocd-e2e-external" @@ -308,7 +308,7 @@ func CreateSecret(username, password string) string { "--from-literal=username="+username, "--from-literal=password="+password, "-n", TestNamespace())) - FailOnErr(Run("", "kubectl", "label", "secret", secretName, testingLabel+"=true", "-n", TestNamespace())) + FailOnErr(Run("", "kubectl", "label", "secret", secretName, TestingLabel+"=true", "-n", TestNamespace())) return secretName } @@ -531,10 +531,11 @@ func EnsureCleanState(t *testing.T) { v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: common.LabelKeySecretType + "=" + common.LabelValueSecretTypeCluster})) // kubectl delete secrets -l e2e.argoproj.io=true CheckError(KubeClientset.CoreV1().Secrets(TestNamespace()).DeleteCollection(context.Background(), - v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: testingLabel + "=true"})) + v1.DeleteOptions{PropagationPolicy: &policy}, v1.ListOptions{LabelSelector: TestingLabel + "=true"})) - FailOnErr(Run("", "kubectl", "delete", "ns", "-l", testingLabel+"=true", "--field-selector", "status.phase=Active", "--wait=false")) - FailOnErr(Run("", "kubectl", "delete", "crd", "-l", testingLabel+"=true", "--wait=false")) + FailOnErr(Run("", "kubectl", "delete", "ns", "-l", TestingLabel+"=true", "--field-selector", "status.phase=Active", "--wait=false")) + FailOnErr(Run("", "kubectl", "delete", "crd", "-l", TestingLabel+"=true", "--wait=false")) + FailOnErr(Run("", "kubectl", "delete", "clusterroles", "-l", TestingLabel+"=true", "--wait=false")) // reset settings updateSettingConfigMap(func(cm *corev1.ConfigMap) error { @@ -627,7 +628,7 @@ func EnsureCleanState(t *testing.T) { // create namespace FailOnErr(Run("", "kubectl", "create", "ns", DeploymentNamespace())) - FailOnErr(Run("", "kubectl", "label", "ns", DeploymentNamespace(), testingLabel+"=true")) + FailOnErr(Run("", "kubectl", "label", "ns", DeploymentNamespace(), TestingLabel+"=true")) log.WithFields(log.Fields{"duration": time.Since(start), "name": t.Name(), "id": id, "username": "admin", "password": "password"}).Info("clean state") }