@@ -1253,6 +1253,117 @@ var _ = Describe("With a running MachineSync Reconciler", func() {
12531253
12541254 })
12551255
1256+ Context ("Prevent updates to MAPI machine if migrating would be unpredictable" , func () {
1257+ BeforeEach (func () {
1258+ By ("Waiting for VAP to be ready" )
1259+ machineVap = & admissionregistrationv1.ValidatingAdmissionPolicy {}
1260+ Eventually (k8sClient .Get (ctx , client.ObjectKey {Name : "openshift-prevent-migration-when-machine-updating" }, machineVap ), timeout ).Should (Succeed ())
1261+ Eventually (k .Update (machineVap , func () {
1262+ machineVap .Spec .Validations = append (machineVap .Spec .Validations , admissionregistrationv1.Validation {
1263+ Expression : "!(has(object.metadata.labels) && \" test-sentinel\" in object.metadata.labels)" ,
1264+ Message : "policy in place" ,
1265+ })
1266+ })).Should (Succeed ())
1267+
1268+ Eventually (k .Object (machineVap ), timeout ).Should (
1269+ HaveField ("Status.ObservedGeneration" , BeNumerically (">=" , 2 )),
1270+ )
1271+
1272+ By ("Updating the VAP binding" )
1273+ policyBinding = & admissionregistrationv1.ValidatingAdmissionPolicyBinding {}
1274+ Eventually (k8sClient .Get (ctx , client.ObjectKey {
1275+ Name : "openshift-prevent-migration-when-machine-updating" }, policyBinding ), timeout ).Should (Succeed ())
1276+
1277+ Eventually (k .Update (policyBinding , func () {
1278+ policyBinding .Spec .MatchResources .NamespaceSelector .MatchLabels = map [string ]string {
1279+ "kubernetes.io/metadata.name" : mapiNamespace .GetName (),
1280+ }
1281+ }), timeout ).Should (Succeed ())
1282+
1283+ // Wait until the binding shows the patched values
1284+ Eventually (k .Object (policyBinding ), timeout ).Should (
1285+ SatisfyAll (
1286+ HaveField ("Spec.MatchResources.NamespaceSelector.MatchLabels" ,
1287+ HaveKeyWithValue ("kubernetes.io/metadata.name" ,
1288+ mapiNamespace .GetName ())),
1289+ ),
1290+ )
1291+
1292+ By ("Creating a throwaway MAPI machine" )
1293+ sentinelMachine := mapiMachineBuilder .WithName ("sentinel-machine" ).WithAuthoritativeAPI (mapiv1beta1 .MachineAuthorityClusterAPI ).Build ()
1294+ Eventually (k8sClient .Create (ctx , sentinelMachine ), timeout ).Should (Succeed ())
1295+
1296+ capiSentinelMachine := clusterv1resourcebuilder .Machine ().WithName ("sentinel-machine" ).WithNamespace (capiNamespace .Name ).Build ()
1297+ Expect (k8sClient .Create (ctx , capiSentinelMachine )).To (Succeed ())
1298+
1299+ Eventually (k .Get (capiSentinelMachine )).Should (Succeed ())
1300+
1301+ Eventually (k .Update (sentinelMachine , func () {
1302+ sentinelMachine .ObjectMeta .Labels = map [string ]string {"test-sentinel" : "fubar" }
1303+ }), timeout ).Should (MatchError (ContainSubstring ("policy in place" )))
1304+ })
1305+
1306+ It ("denies updating the AuthoritativeAPI when the machine is in Provisioning" , func () {
1307+ By ("Updating the MAPI machine phase to be provisioning" )
1308+ Eventually (k .UpdateStatus (mapiMachine , func () {
1309+ provisioningPhase := mapiv1beta1 .PhaseProvisioning
1310+ mapiMachine .Status .Phase = & provisioningPhase
1311+ })).Should (Succeed ())
1312+
1313+ By ("Attempting to update the authoritativeAPI should be blocked" )
1314+ Eventually (k .Update (mapiMachine , func () {
1315+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1316+ }), timeout ).Should (MatchError (ContainSubstring ("Cannot update .spec.authoritativeAPI when machine is in Provisioning phase" )))
1317+ })
1318+
1319+ It ("denies updating the AuthoritativeAPI when the machine has a non-zero deletion timestamp" , func () {
1320+ By ("Adding a finalizer to prevent actual deletion" )
1321+ Eventually (k .Update (mapiMachine , func () {
1322+ mapiMachine .Finalizers = append (mapiMachine .Finalizers , "test-finalizer" )
1323+ })).Should (Succeed ())
1324+
1325+ By ("Deleting the MAPI machine to set deletion timestamp" )
1326+ Eventually (k8sClient .Delete (ctx , mapiMachine )).Should (Succeed ())
1327+
1328+ By ("Waiting for deletion timestamp to be set" )
1329+ Eventually (k .Object (mapiMachine )).Should (SatisfyAll (
1330+ HaveField ("DeletionTimestamp" , Not (BeNil ())),
1331+ ))
1332+
1333+ By ("Attempting to update the authoritativeAPI should be blocked" )
1334+ Eventually (k .Update (mapiMachine , func () {
1335+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1336+ }), timeout ).Should (MatchError (ContainSubstring ("Cannot update .spec.authoritativeAPI when machine has a non-zero deletion timestamp" )))
1337+ })
1338+
1339+ It ("allows updating the AuthoritativeAPI when the machine is in Running phase" , func () {
1340+ By ("Updating the MAPI machine phase to be running" )
1341+ Eventually (k .UpdateStatus (mapiMachine , func () {
1342+ runningPhase := mapiv1beta1 .PhaseRunning
1343+ mapiMachine .Status .Phase = & runningPhase
1344+ })).Should (Succeed ())
1345+
1346+ By ("Attempting to update the authoritativeAPI should succeed" )
1347+ Eventually (k .Update (mapiMachine , func () {
1348+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1349+ }), timeout ).Should (Succeed ())
1350+ })
1351+
1352+ It ("allows updating labels when the machine is in Provisioning phase but not changing AuthoritativeAPI" , func () {
1353+ By ("Updating the MAPI machine phase to be provisioning" )
1354+ Eventually (k .UpdateStatus (mapiMachine , func () {
1355+ provisioningPhase := mapiv1beta1 .PhaseProvisioning
1356+ mapiMachine .Status .Phase = & provisioningPhase
1357+ })).Should (Succeed ())
1358+
1359+ By ("Attempting to update labels should succeed" )
1360+ Eventually (k .Update (mapiMachine , func () {
1361+ mapiMachine .ObjectMeta .Labels = map [string ]string {"test-label" : "fubar" }
1362+ }), timeout ).Should (Succeed ())
1363+ })
1364+
1365+ })
1366+
12561367 })
12571368})
12581369
0 commit comments