Skip to content

Commit

Permalink
test: add server-side apply e2e test
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhiying Lin committed May 22, 2024
1 parent 5893c9c commit 5ee70b8
Showing 1 changed file with 236 additions and 0 deletions.
236 changes: 236 additions & 0 deletions test/e2e/placement_apply_strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
"go.goms.io/fleet/pkg/controllers/work"
)

const (
e2eTestFieldManager = "e2e-test-field-manager"
)

var _ = Describe("validating CRP when resources exists", Ordered, func() {
crpName := fmt.Sprintf(crpNameTemplate, GinkgoParallelProcess())
annotationKey := "annotation-key"
annotationValue := "annotation-value"
annotationUpdatedValue := "annotation-updated-value"
workNamespaceName := fmt.Sprintf(workNamespaceNameTemplate, GinkgoParallelProcess())

BeforeAll(func() {
Expand Down Expand Up @@ -154,6 +160,236 @@ var _ = Describe("validating CRP when resources exists", Ordered, func() {
Eventually(finalizerRemovedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove controller finalizers from CRP %s", crpName)
})
})

Context("Test a CRP fail to apply namespace when the conflicted annotation is managed by others (server-side-apply and allow co-own)", Ordered, func() {
BeforeAll(func() {
ns := appNamespace()
ns.SetOwnerReferences([]metav1.OwnerReference{
{
APIVersion: "another-api-version",
Kind: "another-kind",
Name: "another-owner",
UID: "another-uid",
},
})
ns.Annotations = map[string]string{
annotationKey: annotationValue,
}
options := client.CreateOptions{FieldManager: e2eTestFieldManager}
By(fmt.Sprintf("creating namespace %s on member cluster", ns.Name))
Expect(allMemberClusters[0].KubeClient.Create(ctx, &ns, &options)).Should(Succeed(), "Failed to create namespace %s", ns.Name)

By(fmt.Sprintf("updating namespace %s annotation on hub cluster", ns.Name))
Expect(hubClient.Get(ctx, types.NamespacedName{Name: workNamespaceName}, &ns)).Should(Succeed(), "Failed to get namespace %s", workNamespaceName)
ns.Annotations = map[string]string{
annotationKey: annotationUpdatedValue,
}
Expect(hubClient.Update(ctx, &ns)).Should(Succeed(), "Failed to update namespace %s", workNamespaceName)

// Create the CRP.
strategy := &placementv1beta1.ApplyStrategy{
Type: placementv1beta1.ApplyStrategyTypeServerSideApply,
AllowCoOwnership: true,
}
createCRP(crpName, strategy)
})

AfterAll(func() {
By(fmt.Sprintf("deleting placement %s", crpName))
cleanupCRP(crpName)

By("deleting created work resources on member cluster")
cleanWorkResourcesOnCluster(allMemberClusters[0])
})

It("should update CRP status as expected", func() {
crpStatusUpdatedActual := func() error {
crp := &placementv1beta1.ClusterResourcePlacement{}
if err := hubClient.Get(ctx, types.NamespacedName{Name: crpName}, crp); err != nil {
return err
}

workNamespaceName := fmt.Sprintf(workNamespaceNameTemplate, GinkgoParallelProcess())
appConfigMapName := fmt.Sprintf(appConfigMapNameTemplate, GinkgoParallelProcess())
wantStatus := placementv1beta1.ClusterResourcePlacementStatus{
Conditions: crpAppliedFailedConditions(crp.Generation),
PlacementStatuses: []placementv1beta1.ResourcePlacementStatus{
{
ClusterName: allMemberClusters[0].ClusterName,
FailedPlacements: []placementv1beta1.FailedResourcePlacement{
{
ResourceIdentifier: placementv1beta1.ResourceIdentifier{
Kind: "Namespace",
Name: workNamespaceName,
Version: "v1",
},
Condition: metav1.Condition{
Type: placementv1beta1.WorkConditionTypeApplied,
Status: metav1.ConditionFalse,
Reason: work.ManifestApplyFailedReason,
ObservedGeneration: 0,
},
},
},
Conditions: resourcePlacementApplyFailedConditions(crp.Generation),
},
{
ClusterName: allMemberClusters[1].ClusterName,
Conditions: resourcePlacementRolloutCompletedConditions(crp.Generation, true, false),
},
{
ClusterName: allMemberClusters[2].ClusterName,
Conditions: resourcePlacementRolloutCompletedConditions(crp.Generation, true, false),
},
},
SelectedResources: []placementv1beta1.ResourceIdentifier{
{
Kind: "Namespace",
Name: workNamespaceName,
Version: "v1",
},
{
Kind: "ConfigMap",
Name: appConfigMapName,
Version: "v1",
Namespace: workNamespaceName,
},
},
ObservedResourceIndex: "0",
}
if diff := cmp.Diff(crp.Status, wantStatus, crpStatusCmpOptions...); diff != "" {
return fmt.Errorf("CRP status diff (-got, +want): %s", diff)
}
return nil
}
Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP %s status as expected", crpName)
})

// This check will ignore the annotation of resources.
It("should place the selected resources on member clusters", checkIfPlacedWorkResourcesOnAllMemberClusters)

It("should have original annotation on the namespace of first cluster", func() {
want := map[string]string{annotationKey: annotationValue}
Expect(validateAnnotationOfWorkNamespaceOnCluster(allMemberClusters[0], want)).Should(Succeed(), "Failed to override the annotation of work namespace on %s", allMemberClusters[0].ClusterName)
})

It("should have updated annotations on the namespace of all clusters excluding the first one", func() {
want := map[string]string{annotationKey: annotationUpdatedValue}
for _, c := range allMemberClusters[1:] {
Expect(validateAnnotationOfWorkNamespaceOnCluster(c, want)).Should(Succeed(), "Failed to override the annotation of work namespace on %s", c.ClusterName)
}
})

It("can delete the CRP", func() {
// Delete the CRP.
crp := &placementv1beta1.ClusterResourcePlacement{
ObjectMeta: metav1.ObjectMeta{
Name: crpName,
},
}
Expect(hubClient.Delete(ctx, crp)).To(Succeed(), "Failed to delete CRP %s", crpName)
})

It("should remove placed resources from member clusters excluding the first one", func() {
checkIfRemovedWorkResourcesFromMemberClusters(allMemberClusters[1:])
})

It("should remove controller finalizers from CRP", func() {
finalizerRemovedActual := allFinalizersExceptForCustomDeletionBlockerRemovedFromCRPActual(crpName)
Eventually(finalizerRemovedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove controller finalizers from CRP %s", crpName)
})

It("namespace should be kept on member cluster", func() {
Consistently(func() error {
ns := &corev1.Namespace{}
return allMemberClusters[0].KubeClient.Get(ctx, types.NamespacedName{Name: workNamespaceName}, ns)
}, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Namespace which is not owned by the CRP should not be deleted")
})
})

Context("Test a CRP able to apply namespace when the conflicted annotation is managed by others (force server-side-apply and allow co-own)", Ordered, func() {
BeforeAll(func() {
ns := appNamespace()
ns.SetOwnerReferences([]metav1.OwnerReference{
{
APIVersion: "another-api-version",
Kind: "another-kind",
Name: "another-owner",
UID: "another-uid",
},
})
ns.Annotations = map[string]string{
annotationKey: annotationValue,
}
options := client.CreateOptions{FieldManager: e2eTestFieldManager}
By(fmt.Sprintf("creating namespace %s on member cluster", ns.Name))
Expect(allMemberClusters[0].KubeClient.Create(ctx, &ns, &options)).Should(Succeed(), "Failed to create namespace %s", ns.Name)

By(fmt.Sprintf("updating namespace %s annotation on hub cluster", ns.Name))
Expect(hubClient.Get(ctx, types.NamespacedName{Name: workNamespaceName}, &ns)).Should(Succeed(), "Failed to get namespace %s", workNamespaceName)
ns.Annotations = map[string]string{
annotationKey: annotationUpdatedValue,
}
Expect(hubClient.Update(ctx, &ns)).Should(Succeed(), "Failed to update namespace %s", workNamespaceName)

// Create the CRP.
strategy := &placementv1beta1.ApplyStrategy{
Type: placementv1beta1.ApplyStrategyTypeServerSideApply,
ServerSideApplyConfig: &placementv1beta1.ServerSideApplyConfig{ForceConflicts: true},
AllowCoOwnership: true,
}
createCRP(crpName, strategy)
})

AfterAll(func() {
By(fmt.Sprintf("deleting placement %s", crpName))
cleanupCRP(crpName)

By("deleting created work resources on member cluster")
cleanWorkResourcesOnCluster(allMemberClusters[0])
})

It("should update CRP status as expected", func() {
crpStatusUpdatedActual := crpStatusUpdatedActual(workResourceIdentifiers(), allMemberClusterNames, nil, "0")
Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP %s status as expected", crpName)
})

// This check will ignore the annotation of resources.
It("should place the selected resources on member clusters", checkIfPlacedWorkResourcesOnAllMemberClusters)

It("should have updated annotations on the namespace of all clusters", func() {
want := map[string]string{annotationKey: annotationUpdatedValue}
for _, c := range allMemberClusters {
Expect(validateAnnotationOfWorkNamespaceOnCluster(c, want)).Should(Succeed(), "Failed to override the annotation of work namespace on %s", c.ClusterName)
}
})

It("can delete the CRP", func() {
// Delete the CRP.
crp := &placementv1beta1.ClusterResourcePlacement{
ObjectMeta: metav1.ObjectMeta{
Name: crpName,
},
}
Expect(hubClient.Delete(ctx, crp)).To(Succeed(), "Failed to delete CRP %s", crpName)
})

It("should remove placed resources from member clusters excluding the first one", func() {
checkIfRemovedWorkResourcesFromMemberClusters(allMemberClusters[1:])
})

It("should remove controller finalizers from CRP", func() {
finalizerRemovedActual := allFinalizersExceptForCustomDeletionBlockerRemovedFromCRPActual(crpName)
Eventually(finalizerRemovedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove controller finalizers from CRP %s", crpName)
})

It("namespace should be kept on member cluster", func() {
Consistently(func() error {
ns := &corev1.Namespace{}
return allMemberClusters[0].KubeClient.Get(ctx, types.NamespacedName{Name: workNamespaceName}, ns)
}, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Namespace which is not owned by the CRP should not be deleted")
})
})
})

var _ = Describe("validating two CRP selecting the same resources", Ordered, func() {
Expand Down

0 comments on commit 5ee70b8

Please sign in to comment.