diff --git a/test/e2e/work_api_e2e_test.go b/test/e2e/work_api_e2e_test.go index fcaa0498c..5f6d52b94 100644 --- a/test/e2e/work_api_e2e_test.go +++ b/test/e2e/work_api_e2e_test.go @@ -21,7 +21,6 @@ import ( testutils "go.goms.io/fleet/test/e2e/utils" ) -// TODO: enable this when join/leave logic is connected to work-api, join the Hub and Member for this test. var _ = Describe("Work API Controller test", func() { const ( @@ -46,7 +45,8 @@ var _ = Describe("Work API Controller test", func() { "CreationTimestamp", "Annotations", "OwnerReferences", - "ManagedFields"), + "ManagedFields", + "Labels"), } appliedWorkCmpOptions = append(cmpOptions, cmpopts.IgnoreFields(workapi.AppliedResourceMeta{}, "UID")) @@ -55,10 +55,16 @@ var _ = Describe("Work API Controller test", func() { cmpopts.IgnoreFields(apiextensionsv1.CustomResourceDefinition{}, "Status"), cmpopts.IgnoreFields(apiextensionsv1.CustomResourceDefinitionSpec{}, "Versions", "Conversion")) + namespaceCmpOptions = append(cmpOptions, + cmpopts.IgnoreFields(corev1.Namespace{}, "Spec", "Status")) + secretCmpOptions = append(cmpOptions, cmpopts.IgnoreFields(corev1.Secret{}, "TypeMeta"), ) + serviceAccountCmpOptions = append(cmpOptions, + cmpopts.IgnoreFields(corev1.ServiceAccount{}, "TypeMeta", "Secrets")) + resourceNamespace *corev1.Namespace ) @@ -73,9 +79,6 @@ var _ = Describe("Work API Controller test", func() { }, } testutils.CreateNamespace(*MemberCluster, resourceNamespace) - - //Empties the works since they were garbage collected earlier. - works = []workapi.Work{} }) AfterEach(func() { @@ -360,6 +363,235 @@ var _ = Describe("Work API Controller test", func() { Expect(retrievedSecret.ObjectMeta.Annotations[specHashAnnotation]).ToNot(BeEmpty(), "SpecHash Annotation does not exist for resource %s", secret.Name) }) + + It("Manifests with dependencies within different work objects should successfully apply", func() { + + workNameForNamespace := testutils.RandomWorkName(5) + workNameForServiceAccount := testutils.RandomWorkName(6) + + testNamespace := corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + // Ns abbreviated to avoid duplicate wording + nsNamespaceType := types.NamespacedName{ + Name: testNamespace.Name, + } + + testServiceAccount := corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service-account", + Namespace: testNamespace.Name, + }, + } + serviceAccountNamespaceType := types.NamespacedName{ + Name: testServiceAccount.Name, + Namespace: testNamespace.Name, + } + + namespaceTypeForNamespace := types.NamespacedName{Name: workNameForNamespace, Namespace: workNamespace.Name} + namespaceTypeForServiceAccount := types.NamespacedName{Name: workNameForServiceAccount, Namespace: workNamespace.Name} + + manifestNamespace := testutils.AddManifests([]runtime.Object{&testNamespace}, []workapi.Manifest{}) + manifestServiceAccount := testutils.AddManifests([]runtime.Object{&testServiceAccount}, []workapi.Manifest{}) + + workForNamespace := testutils.CreateWork(ctx, *HubCluster, workNameForNamespace, workNamespace.Name, manifestNamespace) + workForServiceAccount := testutils.CreateWork(ctx, *HubCluster, workNameForServiceAccount, workNamespace.Name, manifestServiceAccount) + + By(fmt.Sprintf("Applied Condition should be set to True for Work %s and %s", namespaceTypeForNamespace, namespaceTypeForServiceAccount)) + + wantAppliedCondition := []metav1.Condition{ + { + Type: conditionTypeApplied, + Status: metav1.ConditionTrue, + Reason: "appliedWorkComplete", + }, + } + + receivedWorkForNamespace := workapi.Work{} + receivedWorkForServiceAccount := workapi.Work{} + + Eventually(func() string { + if err := HubCluster.KubeClient.Get(ctx, namespaceTypeForNamespace, &receivedWorkForNamespace); err != nil { + return err.Error() + } + + return cmp.Diff(wantAppliedCondition, receivedWorkForNamespace.Status.Conditions, cmpOptions...) + }, testutils.PollTimeout, testutils.PollInterval).Should(BeEmpty(), + "Validate WorkStatus for work %s mismatch (-want, +got):", workForNamespace) + + Eventually(func() string { + if err := HubCluster.KubeClient.Get(ctx, namespaceTypeForServiceAccount, &receivedWorkForServiceAccount); err != nil { + return err.Error() + } + + return cmp.Diff(wantAppliedCondition, receivedWorkForServiceAccount.Status.Conditions, cmpOptions...) + }, testutils.PollTimeout, testutils.PollInterval).Should(BeEmpty(), + "Validate WorkStatus for work %s mismatch (-want, +got):", workForServiceAccount) + + By(fmt.Sprintf("Manifest Condiitons on Work Objects %s and %s should be applied", namespaceTypeForNamespace, namespaceTypeForServiceAccount)) + wantNamespaceManifestCondition := []workapi.ManifestCondition{ + { + Conditions: []metav1.Condition{ + { + Type: conditionTypeApplied, + Status: metav1.ConditionTrue, + Reason: "appliedManifestUpdated", + }, + }, + Identifier: workapi.ResourceIdentifier{ + Group: testNamespace.GroupVersionKind().Group, + Version: testNamespace.GroupVersionKind().Version, + Kind: testNamespace.GroupVersionKind().Kind, + Namespace: testNamespace.Namespace, + Name: testNamespace.Name, + Resource: "namespaces", + }, + }, + } + wantServiceAccountManifestCondition := []workapi.ManifestCondition{ + { + Conditions: []metav1.Condition{ + { + Type: conditionTypeApplied, + Status: metav1.ConditionTrue, + Reason: "appliedManifestUpdated", + }, + }, + Identifier: workapi.ResourceIdentifier{ + Group: testServiceAccount.GroupVersionKind().Group, + Version: testServiceAccount.GroupVersionKind().Version, + Kind: testServiceAccount.GroupVersionKind().Kind, + Namespace: testServiceAccount.Namespace, + Name: testServiceAccount.Name, + Resource: "serviceaccounts", + }, + }, + } + + Expect(cmp.Diff(wantNamespaceManifestCondition, receivedWorkForNamespace.Status.ManifestConditions, cmpOptions...)).Should(BeEmpty(), + "Manifest Condition not matching for work %s (-want, +got):", namespaceTypeForNamespace) + + Expect(cmp.Diff(wantServiceAccountManifestCondition, receivedWorkForServiceAccount.Status.ManifestConditions, cmpOptions...)).Should(BeEmpty(), + "Manifest Condition not matching for work %s (-want, +got):", namespaceTypeForServiceAccount) + + By(fmt.Sprintf("AppliedWorkStatus should contain the meta for the resource %s and %s", testNamespace.Name, testServiceAccount.Name)) + appliedWorkForNamespace := workapi.AppliedWork{} + Expect(MemberCluster.KubeClient.Get(ctx, namespaceTypeForNamespace, &appliedWorkForNamespace)).Should(Succeed(), + "Retrieving AppliedWork %s failed", workNameForNamespace) + + wantAppliedWorkConditionNamespace := workapi.AppliedtWorkStatus{ + AppliedResources: []workapi.AppliedResourceMeta{ + { + ResourceIdentifier: workapi.ResourceIdentifier{ + Group: testNamespace.GroupVersionKind().Group, + Version: testNamespace.GroupVersionKind().Version, + Kind: testNamespace.GroupVersionKind().Kind, + Namespace: testNamespace.Namespace, + Name: testNamespace.Name, + Resource: "namespaces", + }, + }, + }, + } + + Expect(cmp.Diff(wantAppliedWorkConditionNamespace, appliedWorkForNamespace.Status, appliedWorkCmpOptions...)).Should(BeEmpty(), + "Validate AppliedResourceMeta mismatch for appliedWork %s (-want, +got):", appliedWorkForNamespace.Name) + + appliedWorkForServiceAccount := workapi.AppliedWork{} + Expect(MemberCluster.KubeClient.Get(ctx, namespaceTypeForServiceAccount, &appliedWorkForServiceAccount)).Should(Succeed(), + "Retrieving AppliedWork %s failed", workNameForServiceAccount) + + wantAppliedConditionServiceAccount := workapi.AppliedtWorkStatus{ + AppliedResources: []workapi.AppliedResourceMeta{ + { + ResourceIdentifier: workapi.ResourceIdentifier{ + Group: testServiceAccount.GroupVersionKind().Group, + Version: testServiceAccount.GroupVersionKind().Version, + Kind: testServiceAccount.GroupVersionKind().Kind, + Namespace: testServiceAccount.Namespace, + Name: testServiceAccount.Name, + Resource: "serviceaccounts", + }, + }, + }, + } + + Expect(cmp.Diff(wantAppliedConditionServiceAccount, appliedWorkForServiceAccount.Status, appliedWorkCmpOptions...)).Should(BeEmpty(), + "Validate AppliedResourceMeta mismatch for appliedWork %s (-want, +got):", appliedWorkForServiceAccount.Name) + + By(fmt.Sprintf("The resources %s and %s should both be created in the member cluster.", testNamespace.Name, testServiceAccount.Name)) + retrievedNamespace := corev1.Namespace{} + Expect(MemberCluster.KubeClient.Get(ctx, nsNamespaceType, &retrievedNamespace)).Should(Succeed(), + "Failed in retrieving resource %s", testNamespace) + wantNamespace := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + + Expect(cmp.Diff(wantNamespace, retrievedNamespace, namespaceCmpOptions...)).Should(BeEmpty(), + "Validate Namespace %s mismatch (-want, +got):", wantNamespace.Name) + + retrievedServiceAccount := corev1.ServiceAccount{} + Expect(MemberCluster.KubeClient.Get(ctx, serviceAccountNamespaceType, &retrievedServiceAccount)).Should(Succeed(), + "Failed in retrieving resource %s", testServiceAccount) + + wantServiceAccount := corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-service-account", + Namespace: testNamespace.Name, + }, + } + + Expect(cmp.Diff(wantServiceAccount, retrievedServiceAccount, serviceAccountCmpOptions...)).Should(BeEmpty(), + "Validate Service Account %s mismatch (-want, +got):", wantServiceAccount.Name) + + By(fmt.Sprintf("Validating that the resource %s and %s is owned by the respective work", testNamespace.Name, testServiceAccount.Name)) + wantOwnerForNamespace := []metav1.OwnerReference{ + { + APIVersion: workapi.GroupVersion.String(), + Kind: workapi.AppliedWorkKind, + Name: appliedWorkForNamespace.GetName(), + UID: appliedWorkForNamespace.GetUID(), + }, + } + + wantOwnerForServiceAccount := []metav1.OwnerReference{ + { + APIVersion: workapi.GroupVersion.String(), + Kind: workapi.AppliedWorkKind, + Name: appliedWorkForServiceAccount.GetName(), + UID: appliedWorkForServiceAccount.GetUID(), + }, + } + + Expect(cmp.Diff(wantOwnerForNamespace, retrievedNamespace.OwnerReferences, cmpOptions...)).Should(BeEmpty(), + "OwnerReference mismatch for resource %s (-want, +got):", testNamespace.Name) + Expect(cmp.Diff(wantOwnerForServiceAccount, retrievedServiceAccount.OwnerReferences, cmpOptions...)).Should(BeEmpty(), + "OwnerReference mismatch for resource %s (-want, +got):", testServiceAccount.Name) + + // TODO: spec has isn't being created when resources such as namespace and service account is being created by the work-api. Possible bug? + //Expect(testNamespace.ObjectMeta.Annotations[specHashAnnotation]).ToNot(BeEmpty(), + // "SpecHash Annotation does not exist for resource %s", testNamespace.Name) + //Expect(testServiceAccount.ObjectMeta.Annotations[specHashAnnotation]).ToNot(BeEmpty(), + // "SpecHash Annotation does not exist for resource %s", testServiceAccount.Name) + + }) + It("Upon successful work creation of a CRD resource, manifest is applied, and resources are created", func() { workName := testutils.RandomWorkName(5)