@@ -18,6 +18,7 @@ package e2e
1818
1919import (
2020 "fmt"
21+ "os/exec"
2122 "time"
2223
2324 . "github.com/onsi/ginkgo/v2"
@@ -1033,6 +1034,127 @@ var _ = Describe("test CRP rollout with staged update run", func() {
10331034 })
10341035 })
10351036
1037+ Context ("Test kubectl-fleet approve plugin with cluster approval requests" , Ordered , func () {
1038+ var strategy * placementv1beta1.ClusterStagedUpdateStrategy
1039+ updateRunName := fmt .Sprintf (updateRunNameWithSubIndexTemplate , GinkgoParallelProcess (), 0 )
1040+
1041+ BeforeAll (func () {
1042+ // Create a test namespace and a configMap inside it on the hub cluster.
1043+ createWorkResources ()
1044+
1045+ // Create the CRP with external rollout strategy.
1046+ crp := & placementv1beta1.ClusterResourcePlacement {
1047+ ObjectMeta : metav1.ObjectMeta {
1048+ Name : crpName ,
1049+ // Add a custom finalizer; this would allow us to better observe
1050+ // the behavior of the controllers.
1051+ Finalizers : []string {customDeletionBlockerFinalizer },
1052+ },
1053+ Spec : placementv1beta1.PlacementSpec {
1054+ ResourceSelectors : workResourceSelector (),
1055+ Strategy : placementv1beta1.RolloutStrategy {
1056+ Type : placementv1beta1 .ExternalRolloutStrategyType ,
1057+ },
1058+ },
1059+ }
1060+ Expect (hubClient .Create (ctx , crp )).To (Succeed (), "Failed to create CRP" )
1061+
1062+ // Create the clusterStagedUpdateStrategy.
1063+ strategy = createStagedUpdateStrategySucceed (strategyName )
1064+ })
1065+
1066+ AfterAll (func () {
1067+ // Remove the custom deletion blocker finalizer from the CRP.
1068+ ensureCRPAndRelatedResourcesDeleted (crpName , allMemberClusters )
1069+
1070+ // Delete the clusterStagedUpdateRun.
1071+ ensureUpdateRunDeletion (updateRunName )
1072+
1073+ // Delete the clusterStagedUpdateStrategy.
1074+ ensureUpdateRunStrategyDeletion (strategyName )
1075+ })
1076+
1077+ It ("Should create a staged update run and verify cluster approval request is created" , func () {
1078+ validateLatestResourceSnapshot (crpName , resourceSnapshotIndex1st )
1079+ validateLatestPolicySnapshot (crpName , policySnapshotIndex1st , 3 )
1080+ createStagedUpdateRunSucceed (updateRunName , crpName , resourceSnapshotIndex1st , strategyName )
1081+
1082+ // Verify that cluster approval request is created for canary stage.
1083+ Eventually (func () error {
1084+ appReqList := & placementv1beta1.ClusterApprovalRequestList {}
1085+ if err := hubClient .List (ctx , appReqList , client.MatchingLabels {
1086+ placementv1beta1 .TargetUpdatingStageNameLabel : envCanary ,
1087+ placementv1beta1 .TargetUpdateRunLabel : updateRunName ,
1088+ }); err != nil {
1089+ return fmt .Errorf ("failed to list approval requests: %w" , err )
1090+ }
1091+
1092+ if len (appReqList .Items ) != 1 {
1093+ return fmt .Errorf ("want 1 approval request, got %d" , len (appReqList .Items ))
1094+ }
1095+ return nil
1096+ }, eventuallyDuration , eventuallyInterval ).Should (Succeed (), "Failed to find cluster approval request" )
1097+ })
1098+
1099+ It ("Should approve cluster approval request using kubectl-fleet approve plugin" , func () {
1100+ var approvalRequestName string
1101+
1102+ // Get the cluster approval request name.
1103+ Eventually (func () error {
1104+ appReqList := & placementv1beta1.ClusterApprovalRequestList {}
1105+ if err := hubClient .List (ctx , appReqList , client.MatchingLabels {
1106+ placementv1beta1 .TargetUpdatingStageNameLabel : envCanary ,
1107+ placementv1beta1 .TargetUpdateRunLabel : updateRunName ,
1108+ }); err != nil {
1109+ return fmt .Errorf ("failed to list approval requests: %w" , err )
1110+ }
1111+
1112+ if len (appReqList .Items ) != 1 {
1113+ return fmt .Errorf ("want 1 approval request, got %d" , len (appReqList .Items ))
1114+ }
1115+
1116+ approvalRequestName = appReqList .Items [0 ].Name
1117+ return nil
1118+ }, eventuallyDuration , eventuallyInterval ).Should (Succeed (), "Failed to get approval request name" )
1119+
1120+ // Use kubectl-fleet approve plugin to approve the request
1121+ cmd := exec .Command (fleetBinaryPath , "approve" , "clusterapprovalrequest" ,
1122+ "--hubClusterContext" , "kind-hub" ,
1123+ "--name" , approvalRequestName )
1124+ output , err := cmd .CombinedOutput ()
1125+ Expect (err ).ToNot (HaveOccurred (), "kubectl-fleet approve failed: %s" , string (output ))
1126+
1127+ // Verify the approval request is approved
1128+ Eventually (func () error {
1129+ var appReq placementv1beta1.ClusterApprovalRequest
1130+ if err := hubClient .Get (ctx , client.ObjectKey {Name : approvalRequestName }, & appReq ); err != nil {
1131+ return fmt .Errorf ("failed to get approval request: %w" , err )
1132+ }
1133+
1134+ approvedCondition := meta .FindStatusCondition (appReq .Status .Conditions , string (placementv1beta1 .ApprovalRequestConditionApproved ))
1135+ if approvedCondition == nil {
1136+ return fmt .Errorf ("approved condition not found" )
1137+ }
1138+ if approvedCondition .Status != metav1 .ConditionTrue {
1139+ return fmt .Errorf ("approved condition status is %s, want True" , approvedCondition .Status )
1140+ }
1141+ return nil
1142+ }, eventuallyDuration , eventuallyInterval ).Should (Succeed (), "Failed to verify approval request is approved" )
1143+ })
1144+
1145+ It ("Should complete the staged update run after approval" , func () {
1146+ updateRunSucceededActual := updateRunStatusSucceededActual (updateRunName , policySnapshotIndex1st , len (allMemberClusters ), defaultApplyStrategy , & strategy .Spec , [][]string {{allMemberClusterNames [1 ]}, {allMemberClusterNames [0 ], allMemberClusterNames [2 ]}}, nil , nil , nil )
1147+ Eventually (updateRunSucceededActual , updateRunEventuallyDuration , eventuallyInterval ).Should (Succeed (), "Failed to validate updateRun %s succeeded" , updateRunName )
1148+ checkIfPlacedWorkResourcesOnMemberClustersInUpdateRun (allMemberClusters )
1149+ })
1150+
1151+ It ("Should update crp status as completed" , func () {
1152+ crpStatusUpdatedActual := crpStatusWithExternalStrategyActual (workResourceIdentifiers (), resourceSnapshotIndex1st , true , allMemberClusterNames ,
1153+ []string {resourceSnapshotIndex1st , resourceSnapshotIndex1st , resourceSnapshotIndex1st }, []bool {true , true , true }, nil , nil )
1154+ Eventually (crpStatusUpdatedActual , eventuallyDuration , eventuallyInterval ).Should (Succeed (), "Failed to update CRP %s status as expected" , crpName )
1155+ })
1156+ })
1157+
10361158 Context ("Test CRP rollout strategy transition from external to rollingUpdate" , Ordered , func () {
10371159 var strategy * placementv1beta1.ClusterStagedUpdateStrategy
10381160 updateRunName := fmt .Sprintf (updateRunNameWithSubIndexTemplate , GinkgoParallelProcess (), 0 )
0 commit comments