@@ -1876,6 +1876,337 @@ var _ = Describe("With a running MachineSync Reconciler", func() {
18761876 })
18771877 })
18781878
1879+ Context ("when validating MAPI authoritativeAPI transitions" , func () {
1880+ const vapName = "mapi-authapi-transition-requires-capi-ready"
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+ // paramNamespace=capiNamespace (CAPI resources are params)
1901+ // targetNamespace=mapiNamespace (MAPI resources are validated)
1902+ admissiontestutils .UpdateVAPBindingNamespaces (policyBinding , capiNamespace .GetName (), mapiNamespace .GetName ())
1903+ }), timeout ).Should (Succeed ())
1904+
1905+ Eventually (k .Object (policyBinding ), timeout ).Should (
1906+ SatisfyAll (
1907+ HaveField ("Spec.MatchResources.NamespaceSelector.MatchLabels" ,
1908+ HaveKeyWithValue ("kubernetes.io/metadata.name" ,
1909+ mapiNamespace .GetName ())),
1910+ ),
1911+ )
1912+
1913+ By ("Creating sentinel Machine pair for VAP verification" )
1914+ sentinelCapiMachine := clusterv1resourcebuilder .Machine ().
1915+ WithNamespace (capiNamespace .Name ).
1916+ WithName ("sentinel-machine" ).
1917+ Build ()
1918+ Eventually (k8sClient .Create (ctx , sentinelCapiMachine )).Should (Succeed ())
1919+
1920+ sentinelMapiMachine := machinev1resourcebuilder .Machine ().
1921+ WithNamespace (mapiNamespace .Name ).
1922+ WithName ("sentinel-machine" ).
1923+ WithAuthoritativeAPI (mapiv1beta1 .MachineAuthorityMachineAPI ).
1924+ Build ()
1925+ Eventually (k8sClient .Create (ctx , sentinelMapiMachine ), timeout ).Should (Succeed ())
1926+
1927+ admissiontestutils .VerifySentinelValidation (k , sentinelMapiMachine , timeout )
1928+ })
1929+
1930+ Context ("when validating infrastructure readiness" , func () {
1931+ Context ("when infrastructure is not ready" , func () {
1932+ It ("should deny authoritativeAPI change when infrastructureReady is false" , func () {
1933+ By ("Creating CAPI Machine with infrastructureReady=false" )
1934+ Eventually (k .UpdateStatus (capiMachine , func () {
1935+ capiMachine .Status .InfrastructureReady = false
1936+ })).Should (Succeed ())
1937+
1938+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
1939+ Eventually (k .UpdateStatus (mapiMachine , func () {
1940+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
1941+ })).Should (Succeed ())
1942+
1943+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
1944+ Eventually (k .Update (mapiMachine , func () {
1945+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1946+ }), timeout ).Should (MatchError (ContainSubstring ("status.infrastructureReady is true" )))
1947+ })
1948+
1949+ It ("should deny authoritativeAPI change when infrastructureReady is missing" , func () {
1950+ By ("CAPI Machine was created in BeforeEach without setting infrastructureReady" )
1951+
1952+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
1953+ Eventually (k .UpdateStatus (mapiMachine , func () {
1954+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
1955+ })).Should (Succeed ())
1956+
1957+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
1958+ Eventually (k .Update (mapiMachine , func () {
1959+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1960+ }), timeout ).Should (MatchError (ContainSubstring ("status.infrastructureReady is true" )))
1961+ })
1962+
1963+ It ("should deny authoritativeAPI change when status field doesn't exist" , func () {
1964+ By ("CAPI Machine was created in BeforeEach without status" )
1965+
1966+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
1967+ Eventually (k .UpdateStatus (mapiMachine , func () {
1968+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
1969+ })).Should (Succeed ())
1970+
1971+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
1972+ Eventually (k .Update (mapiMachine , func () {
1973+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1974+ }), timeout ).Should (MatchError (ContainSubstring ("status.infrastructureReady is true" )))
1975+ })
1976+ })
1977+
1978+ Context ("when infrastructure is ready" , func () {
1979+ It ("should allow authoritativeAPI change when infrastructureReady is true" , func () {
1980+ By ("Creating CAPI Machine with infrastructureReady=true" )
1981+ Eventually (k .UpdateStatus (capiMachine , func () {
1982+ capiMachine .Status .InfrastructureReady = true
1983+ })).Should (Succeed ())
1984+
1985+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
1986+ Eventually (k .UpdateStatus (mapiMachine , func () {
1987+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
1988+ })).Should (Succeed ())
1989+
1990+ By ("Changing authoritativeAPI to ClusterAPI" )
1991+ Eventually (k .Update (mapiMachine , func () {
1992+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
1993+ }), timeout ).Should (Succeed ())
1994+ })
1995+ })
1996+ })
1997+
1998+ Context ("when validating deletion state" , func () {
1999+ Context ("when CAPI Machine is deleting" , func () {
2000+ It ("should deny authoritativeAPI change when deletionTimestamp is set" , func () {
2001+ By ("Creating CAPI Machine with infrastructureReady=true" )
2002+ Eventually (k .UpdateStatus (capiMachine , func () {
2003+ capiMachine .Status .InfrastructureReady = true
2004+ })).Should (Succeed ())
2005+
2006+ By ("Adding finalizer and deleting CAPI Machine to set deletionTimestamp" )
2007+ Eventually (k .Update (capiMachine , func () {
2008+ capiMachine .Finalizers = append (capiMachine .Finalizers , "test-finalizer" )
2009+ })).Should (Succeed ())
2010+
2011+ Eventually (k8sClient .Delete (ctx , capiMachine )).Should (Succeed ())
2012+
2013+ By ("Waiting for deletion timestamp to be set" )
2014+ Eventually (k .Object (capiMachine )).Should (SatisfyAll (
2015+ HaveField ("DeletionTimestamp" , Not (BeNil ())),
2016+ ))
2017+
2018+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2019+ Eventually (k .UpdateStatus (mapiMachine , func () {
2020+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2021+ })).Should (Succeed ())
2022+
2023+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
2024+ Eventually (k .Update (mapiMachine , func () {
2025+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
2026+ }), timeout ).Should (MatchError (ContainSubstring ("CAPI Machine is being deleted" )))
2027+ })
2028+ })
2029+
2030+ Context ("when CAPI Machine is not deleting" , func () {
2031+ It ("should allow authoritativeAPI change when deletionTimestamp is not set" , func () {
2032+ By ("Creating CAPI Machine with infrastructureReady=true and no deletionTimestamp" )
2033+ Eventually (k .UpdateStatus (capiMachine , func () {
2034+ capiMachine .Status .InfrastructureReady = true
2035+ })).Should (Succeed ())
2036+
2037+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2038+ Eventually (k .UpdateStatus (mapiMachine , func () {
2039+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2040+ })).Should (Succeed ())
2041+
2042+ By ("Changing authoritativeAPI to ClusterAPI" )
2043+ Eventually (k .Update (mapiMachine , func () {
2044+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
2045+ }), timeout ).Should (Succeed ())
2046+ })
2047+ })
2048+ })
2049+
2050+ Context ("when validating mixed states" , func () {
2051+ It ("should deny authoritativeAPI change when infrastructureReady is true but deleting" , func () {
2052+ By ("Creating CAPI Machine with infrastructureReady=true" )
2053+ Eventually (k .UpdateStatus (capiMachine , func () {
2054+ capiMachine .Status .InfrastructureReady = true
2055+ })).Should (Succeed ())
2056+
2057+ By ("Adding finalizer and deleting CAPI Machine to set deletionTimestamp" )
2058+ Eventually (k .Update (capiMachine , func () {
2059+ capiMachine .Finalizers = append (capiMachine .Finalizers , "test-finalizer" )
2060+ })).Should (Succeed ())
2061+
2062+ Eventually (k8sClient .Delete (ctx , capiMachine )).Should (Succeed ())
2063+
2064+ By ("Waiting for deletion timestamp to be set" )
2065+ Eventually (k .Object (capiMachine )).Should (SatisfyAll (
2066+ HaveField ("DeletionTimestamp" , Not (BeNil ())),
2067+ ))
2068+
2069+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2070+ Eventually (k .UpdateStatus (mapiMachine , func () {
2071+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2072+ })).Should (Succeed ())
2073+
2074+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
2075+ Eventually (k .Update (mapiMachine , func () {
2076+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
2077+ }), timeout ).Should (MatchError (ContainSubstring ("CAPI Machine is being deleted" )))
2078+ })
2079+
2080+ It ("should deny authoritativeAPI change when infrastructureReady is false but not deleting" , func () {
2081+ By ("Creating CAPI Machine with infrastructureReady=false" )
2082+ Eventually (k .UpdateStatus (capiMachine , func () {
2083+ capiMachine .Status .InfrastructureReady = false
2084+ })).Should (Succeed ())
2085+
2086+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2087+ Eventually (k .UpdateStatus (mapiMachine , func () {
2088+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2089+ })).Should (Succeed ())
2090+
2091+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
2092+ Eventually (k .Update (mapiMachine , func () {
2093+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
2094+ }), timeout ).Should (MatchError (ContainSubstring ("status.infrastructureReady is true" )))
2095+ })
2096+ })
2097+
2098+ Context ("when testing VAP trigger conditions" , func () {
2099+ Context ("when changing non-authoritativeAPI fields" , func () {
2100+ It ("should allow updating labels without changing authoritativeAPI" , func () {
2101+ By ("Creating CAPI Machine with infrastructureReady=false" )
2102+ Eventually (k .UpdateStatus (capiMachine , func () {
2103+ capiMachine .Status .InfrastructureReady = false
2104+ })).Should (Succeed ())
2105+
2106+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2107+ Eventually (k .UpdateStatus (mapiMachine , func () {
2108+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2109+ })).Should (Succeed ())
2110+
2111+ By ("Updating labels without changing authoritativeAPI" )
2112+ Eventually (k .Update (mapiMachine , func () {
2113+ if mapiMachine .Labels == nil {
2114+ mapiMachine .Labels = make (map [string ]string )
2115+ }
2116+ mapiMachine .Labels ["test-label" ] = "test-value"
2117+ }), timeout ).Should (Succeed ())
2118+ })
2119+
2120+ It ("should allow updating annotations without changing authoritativeAPI" , func () {
2121+ By ("Adding finalizer and deleting CAPI Machine to set deletionTimestamp" )
2122+ Eventually (k .Update (capiMachine , func () {
2123+ capiMachine .Finalizers = append (capiMachine .Finalizers , "test-finalizer" )
2124+ })).Should (Succeed ())
2125+
2126+ Eventually (k8sClient .Delete (ctx , capiMachine )).Should (Succeed ())
2127+
2128+ By ("Waiting for deletion timestamp to be set" )
2129+ Eventually (k .Object (capiMachine )).Should (SatisfyAll (
2130+ HaveField ("DeletionTimestamp" , Not (BeNil ())),
2131+ ))
2132+
2133+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2134+ Eventually (k .UpdateStatus (mapiMachine , func () {
2135+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2136+ })).Should (Succeed ())
2137+
2138+ By ("Updating annotations without changing authoritativeAPI" )
2139+ Eventually (k .Update (mapiMachine , func () {
2140+ if mapiMachine .Annotations == nil {
2141+ mapiMachine .Annotations = make (map [string ]string )
2142+ }
2143+ mapiMachine .Annotations ["test-annotation" ] = "test-value"
2144+ }), timeout ).Should (Succeed ())
2145+ })
2146+
2147+ It ("should allow updating spec fields without changing authoritativeAPI" , func () {
2148+ By ("CAPI Machine was created without infrastructureReady" )
2149+
2150+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2151+ Eventually (k .UpdateStatus (mapiMachine , func () {
2152+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2153+ })).Should (Succeed ())
2154+
2155+ By ("Updating spec.objectMeta.labels without changing authoritativeAPI" )
2156+ Eventually (k .Update (mapiMachine , func () {
2157+ mapiMachine .Spec .ObjectMeta .Labels = map [string ]string {"new-label" : "new-value" }
2158+ }), timeout ).Should (Succeed ())
2159+ })
2160+ })
2161+
2162+ Context ("when CAPI Machine parameter is not found" , func () {
2163+ It ("should allow authoritativeAPI change when CAPI Machine does not exist" , func () {
2164+ By ("Creating a new MAPI Machine without CAPI counterpart" )
2165+ newMapiMachine := machinev1resourcebuilder .Machine ().
2166+ WithNamespace (mapiNamespace .Name ).
2167+ WithName ("no-capi-equivalent" ).
2168+ WithAuthoritativeAPI (mapiv1beta1 .MachineAuthorityMachineAPI ).
2169+ Build ()
2170+ Eventually (k8sClient .Create (ctx , newMapiMachine )).Should (Succeed ())
2171+
2172+ By ("Setting AuthoritativeAPI to MachineAPI" )
2173+ Eventually (k .UpdateStatus (newMapiMachine , func () {
2174+ newMapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2175+ })).Should (Succeed ())
2176+
2177+ By ("Changing authoritativeAPI to ClusterAPI" )
2178+ Eventually (k .Update (newMapiMachine , func () {
2179+ newMapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
2180+ }), timeout ).Should (Succeed ())
2181+ })
2182+
2183+ })
2184+ })
2185+
2186+ Context ("when testing edge cases" , func () {
2187+ Context ("when CAPI Machine is in various states" , func () {
2188+
2189+
2190+ It ("should deny authoritativeAPI change when CAPI Machine is provisioning" , func () {
2191+ By ("Creating CAPI Machine with infrastructureReady=false and no deletionTimestamp" )
2192+ Eventually (k .UpdateStatus (capiMachine , func () {
2193+ capiMachine .Status .InfrastructureReady = false
2194+ })).Should (Succeed ())
2195+
2196+ By ("Setting MAPI machine AuthoritativeAPI to MachineAPI" )
2197+ Eventually (k .UpdateStatus (mapiMachine , func () {
2198+ mapiMachine .Status .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityMachineAPI
2199+ })).Should (Succeed ())
2200+
2201+ By ("Attempting to change authoritativeAPI to ClusterAPI" )
2202+ Eventually (k .Update (mapiMachine , func () {
2203+ mapiMachine .Spec .AuthoritativeAPI = mapiv1beta1 .MachineAuthorityClusterAPI
2204+ }), timeout ).Should (MatchError (ContainSubstring ("status.infrastructureReady is true" )))
2205+ })
2206+ })
2207+ })
2208+ })
2209+
18792210 })
18802211})
18812212
0 commit comments