Skip to content

Commit 34300d6

Browse files
WIP: CAPI Machine creation validation
1 parent 509ccfd commit 34300d6

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

manifests/0000_30_cluster-api_09_admission-policies.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,46 @@ data:
400400
- Deny
401401
---
402402
apiVersion: admissionregistration.k8s.io/v1
403+
kind: ValidatingAdmissionPolicyBinding
404+
metadata:
405+
name: openshift-validate-capi-machine-creation
406+
spec:
407+
matchResources:
408+
namespaceSelector:
409+
matchLabels:
410+
kubernetes.io/metadata.name: openshift-cluster-api
411+
paramRef:
412+
namespace: openshift-machine-api
413+
parameterNotFoundAction: Allow
414+
selector: {}
415+
policyName: openshift-validate-capi-machine-creation
416+
validationActions:
417+
- Deny
418+
---
419+
apiVersion: admissionregistration.k8s.io/v1
420+
kind: ValidatingAdmissionPolicy
421+
metadata:
422+
name: openshift-validate-capi-machine-creation
423+
spec:
424+
failurePolicy: Fail
425+
paramKind:
426+
apiVersion: machine.openshift.io/v1beta1
427+
kind: Machine
428+
matchConstraints:
429+
resourceRules:
430+
- apiGroups: ["cluster.x-k8s.io"]
431+
apiVersions: ["v1beta1"]
432+
operations: ["UPDATE"]
433+
resources: ["machines"]
434+
# Requests must satisfy every matchCondition to reach the validations
435+
matchConditions:
436+
- name: check-param-match
437+
expression: 'object.metadata.name == params.metadata.name'
438+
# All validations must evaluate to true
439+
validations:
440+
- expression: 'true'
441+
---
442+
apiVersion: admissionregistration.k8s.io/v1
403443
kind: ValidatingAdmissionPolicy
404444
metadata:
405445
name: openshift-prevent-migration-when-machine-updating

pkg/controllers/machinesync/machine_sync_controller_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,6 +1876,115 @@ var _ = Describe("With a running MachineSync Reconciler", func() {
18761876
})
18771877
})
18781878

1879+
FContext("cluster api machine validation", func() {
1880+
var vapName = "openshift-validate-capi-machine-creation"
1881+
1882+
BeforeEach(func() {
1883+
By("Waiting for VAP to be ready")
1884+
machineVap = &admissionregistrationv1.ValidatingAdmissionPolicy{}
1885+
Eventually(k8sClient.Get(ctx, client.ObjectKey{Name: vapName}, machineVap), timeout).Should(Succeed())
1886+
Eventually(k.Update(machineVap, func() {
1887+
admissiontestutils.AddSentinelValidation(machineVap)
1888+
})).Should(Succeed())
1889+
1890+
Eventually(k.Object(machineVap), timeout).Should(
1891+
HaveField("Status.ObservedGeneration", BeNumerically(">=", 2)),
1892+
)
1893+
1894+
By("Updating the VAP binding")
1895+
policyBinding = &admissionregistrationv1.ValidatingAdmissionPolicyBinding{}
1896+
Eventually(k8sClient.Get(ctx, client.ObjectKey{
1897+
Name: vapName}, policyBinding), timeout).Should(Succeed())
1898+
1899+
Eventually(k.Update(policyBinding, func() {
1900+
admissiontestutils.UpdateVAPBindingNamespaces(policyBinding, mapiNamespace.GetName(), capiNamespace.GetName())
1901+
}), timeout).Should(Succeed())
1902+
1903+
// Wait until the binding shows the patched values
1904+
Eventually(k.Object(policyBinding), timeout).Should(
1905+
SatisfyAll(
1906+
HaveField("Spec.ParamRef.Namespace",
1907+
Equal(mapiNamespace.GetName())),
1908+
1909+
HaveField("Spec.MatchResources.NamespaceSelector.MatchLabels",
1910+
HaveKeyWithValue("kubernetes.io/metadata.name",
1911+
capiNamespace.GetName())),
1912+
),
1913+
)
1914+
1915+
By("Creating the sentinel CAPI infra machine")
1916+
capaMachine = capaMachineBuilder.WithName("sentinel-machine").Build()
1917+
Eventually(k8sClient.Create(ctx, capaMachine)).Should(Succeed(), "capa machine should be able to be created")
1918+
1919+
capaMachineRef := corev1.ObjectReference{
1920+
Kind: capaMachine.Kind,
1921+
Name: capaMachine.GetName(),
1922+
Namespace: capaMachine.GetNamespace(),
1923+
}
1924+
1925+
By("Creating a sentinel CAPI machine")
1926+
testMachine := capiMachineBuilder.WithName("sentinel-machine").WithInfrastructureRef(capaMachineRef).Build()
1927+
Eventually(k8sClient.Create(ctx, testMachine), timeout).Should(Succeed())
1928+
1929+
By("setting the owner reference on the sentinel CAPI infra machine")
1930+
Eventually(k.Update(capaMachine, func() {
1931+
capaMachine.SetOwnerReferences([]metav1.OwnerReference{
1932+
{
1933+
Kind: machineKind,
1934+
APIVersion: clusterv1.GroupVersion.String(),
1935+
Name: testMachine.Name,
1936+
UID: testMachine.UID,
1937+
BlockOwnerDeletion: ptr.To(true),
1938+
Controller: ptr.To(false),
1939+
},
1940+
})
1941+
})).Should(Succeed())
1942+
1943+
By("Creating a sentinel MAPI Machine")
1944+
testMapiMachine := mapiMachineBuilder.WithName(testMachine.Name).Build()
1945+
Eventually(k8sClient.Create(ctx, testMapiMachine), timeout).Should(Succeed())
1946+
1947+
By("Setting the sentinel MAPI machine AuthoritativeAPI to Machine API")
1948+
Eventually(k.UpdateStatus(testMapiMachine, func() {
1949+
testMapiMachine.Status.AuthoritativeAPI = mapiv1beta1.MachineAuthorityMachineAPI
1950+
})).Should(Succeed())
1951+
1952+
Eventually(k.Object(testMapiMachine), timeout).Should(
1953+
HaveField("Status.AuthoritativeAPI", Equal(mapiv1beta1.MachineAuthorityMachineAPI)))
1954+
1955+
// The sync controller copies labels MAPI → CAPI. Labels under
1956+
// machine.openshift.io/* and cluster.x-k8s.io/* are controller-managed and
1957+
// write-protected by the labels rule. If we update before these labels are
1958+
// populated, our change can drop them and be rejected. Wait for sync, then add
1959+
// the test sentinel.
1960+
1961+
Eventually(k.Object(testMachine), timeout).Should(
1962+
HaveField("ObjectMeta.Labels", Not(BeNil())),
1963+
)
1964+
1965+
Eventually(k.Update(testMachine, func() {
1966+
testMachine.ObjectMeta.Labels["test-sentinel"] = "fubar"
1967+
}), timeout).Should(MatchError(ContainSubstring("policy in place")))
1968+
})
1969+
1970+
Context("with status.authoritativeAPI: Machine API", func() {
1971+
BeforeEach(func() {
1972+
By("Setting the MAPI machine AuthoritativeAPI to Machine API")
1973+
Eventually(k.UpdateStatus(mapiMachine, func() {
1974+
mapiMachine.Status.AuthoritativeAPI = mapiv1beta1.MachineAuthorityMachineAPI
1975+
})).Should(Succeed())
1976+
1977+
Eventually(k.Object(mapiMachine), timeout).Should(
1978+
HaveField("Status.AuthoritativeAPI", Equal(mapiv1beta1.MachineAuthorityMachineAPI)))
1979+
})
1980+
1981+
It("updating the spec should be prevented", func() {
1982+
Eventually(k.Update(capiMachine, func() {
1983+
}), timeout).Should(MatchError(ContainSubstring("Changing .spec is not allowed")))
1984+
})
1985+
})
1986+
})
1987+
18791988
})
18801989
})
18811990

0 commit comments

Comments
 (0)