@@ -1847,6 +1847,94 @@ var _ = Describe("Fake client", func() {
18471847 Expect (cl .Status ().Apply (ctx , node , client .FieldOwner ("test-owner" ))).To (Succeed ())
18481848 })
18491849
1850+ It ("should allow SSA apply on status without object has changed issues" , func (ctx SpecContext ) {
1851+ schemeBuilder := & scheme.Builder {GroupVersion : schema.GroupVersion {Group : "chaosapps.metamagical.io" , Version : "v1" }}
1852+ schemeBuilder .Register (& ChaosPod {})
1853+ testScheme := runtime .NewScheme ()
1854+ Expect (schemeBuilder .AddToScheme (testScheme )).NotTo (HaveOccurred ())
1855+
1856+ customResource := & ChaosPod {
1857+ TypeMeta : metav1.TypeMeta {
1858+ APIVersion : "chaosapps.metamagical.io/v1" ,
1859+ Kind : "ChaosPod" ,
1860+ },
1861+ ObjectMeta : metav1.ObjectMeta {
1862+ Name : "test-chaospod" ,
1863+ Namespace : "default" ,
1864+ },
1865+ Spec : ChaosPodSpec {
1866+ Image : "test-image" ,
1867+ },
1868+ }
1869+ cl := NewClientBuilder ().WithScheme (testScheme ).WithStatusSubresource (customResource ).WithObjects (customResource ).Build ()
1870+
1871+ // Create an unstructured apply configuration with status but no resourceVersion
1872+ resourceForApply := & unstructured.Unstructured {}
1873+ resourceForApply .SetName ("test-chaospod" )
1874+ resourceForApply .SetNamespace ("default" )
1875+ resourceForApply .SetAPIVersion ("chaosapps.metamagical.io/v1" )
1876+ resourceForApply .SetKind ("ChaosPod" )
1877+ resourceForApply .SetResourceVersion ("" )
1878+ Expect (unstructured .SetNestedField (resourceForApply .Object , map [string ]any {"phase" : "Ready" }, "status" )).To (Succeed ())
1879+
1880+ resourceAC := client .ApplyConfigurationFromUnstructured (resourceForApply )
1881+
1882+ err := cl .Status ().Apply (ctx , resourceAC , client .FieldOwner ("test-owner" ), client .ForceOwnership )
1883+ Expect (err ).NotTo (HaveOccurred (), "SSA apply on status should succeed when resourceVersion is not set" )
1884+
1885+ // Verify the status was applied
1886+ finalResource := & ChaosPod {
1887+ TypeMeta : metav1.TypeMeta {
1888+ APIVersion : "chaosapps.metamagical.io/v1" ,
1889+ Kind : "ChaosPod" ,
1890+ },
1891+ ObjectMeta : metav1.ObjectMeta {
1892+ Name : "test-chaospod" ,
1893+ Namespace : "default" ,
1894+ },
1895+ }
1896+ Expect (cl .Get (ctx , client .ObjectKeyFromObject (finalResource ), finalResource )).To (Succeed ())
1897+ Expect (finalResource .Status .Phase ).To (Equal ("Ready" ))
1898+ })
1899+
1900+ It ("should block SSA apply on status when passing the wrong non empty resource version" , func (ctx SpecContext ) {
1901+ schemeBuilder := & scheme.Builder {GroupVersion : schema.GroupVersion {Group : "chaosapps.metamagical.io" , Version : "v1" }}
1902+ schemeBuilder .Register (& ChaosPod {})
1903+ testScheme := runtime .NewScheme ()
1904+ Expect (schemeBuilder .AddToScheme (testScheme )).NotTo (HaveOccurred ())
1905+
1906+ customResource := & ChaosPod {
1907+ TypeMeta : metav1.TypeMeta {
1908+ APIVersion : "chaosapps.metamagical.io/v1" ,
1909+ Kind : "ChaosPod" ,
1910+ },
1911+ ObjectMeta : metav1.ObjectMeta {
1912+ Name : "test-chaospod" ,
1913+ Namespace : "default" ,
1914+ },
1915+ Spec : ChaosPodSpec {
1916+ Image : "test-image" ,
1917+ },
1918+ }
1919+ cl := NewClientBuilder ().WithScheme (testScheme ).WithStatusSubresource (customResource ).WithObjects (customResource ).Build ()
1920+
1921+ // Create an unstructured apply configuration with status but wrong non empty resourceVersion
1922+ resourceForApply := & unstructured.Unstructured {}
1923+ resourceForApply .SetName ("test-chaospod" )
1924+ resourceForApply .SetNamespace ("default" )
1925+ resourceForApply .SetAPIVersion ("chaosapps.metamagical.io/v1" )
1926+ resourceForApply .SetKind ("ChaosPod" )
1927+ resourceForApply .SetResourceVersion ("bad-resource-version" ) // forces the error
1928+ Expect (unstructured .SetNestedField (resourceForApply .Object , map [string ]any {"phase" : "Ready" }, "status" )).To (Succeed ())
1929+
1930+ resourceAC := client .ApplyConfigurationFromUnstructured (resourceForApply )
1931+
1932+ // This is expected to fail with the wrong rv value passed in in the applied config
1933+ err := cl .Status ().Apply (ctx , resourceAC , client .FieldOwner ("test-owner" ), client .ForceOwnership )
1934+ Expect (err ).To (HaveOccurred (), "SSA apply on status should not succeed when resourceVersion is wrongly set" )
1935+ Expect (apierrors .IsConflict (err )).To (BeTrue ())
1936+ })
1937+
18501938 It ("should not be able to manually update the fieldManager through a status update" , func (ctx SpecContext ) {
18511939 node := & corev1.Node {
18521940 ObjectMeta : metav1.ObjectMeta {
@@ -3442,3 +3530,35 @@ func (in *EmbeddedPointerStructCRD) DeepCopyObject() runtime.Object {
34423530
34433531 return & out
34443532}
3533+
3534+ // ChaosPod is a custom resource type used for testing SSA apply operations
3535+ // on custom resources with status subresources.
3536+ type ChaosPod struct {
3537+ metav1.TypeMeta `json:",inline"`
3538+ metav1.ObjectMeta `json:"metadata,omitempty"`
3539+
3540+ Spec ChaosPodSpec `json:"spec,omitempty"`
3541+ Status ChaosPodStatus `json:"status,omitempty"`
3542+ }
3543+
3544+ // ChaosPodSpec defines the spec for ChaosPod
3545+ type ChaosPodSpec struct {
3546+ Image string `json:"image,omitempty"`
3547+ }
3548+
3549+ // ChaosPodStatus defines the status for ChaosPod
3550+ type ChaosPodStatus struct {
3551+ Phase string `json:"phase,omitempty"`
3552+ }
3553+
3554+ func (in * ChaosPod ) DeepCopyObject () runtime.Object {
3555+ if in == nil {
3556+ return nil
3557+ }
3558+ out := & ChaosPod {}
3559+ out .TypeMeta = in .TypeMeta
3560+ out .ObjectMeta = * in .ObjectMeta .DeepCopy ()
3561+ out .Spec = in .Spec
3562+ out .Status = in .Status
3563+ return out
3564+ }
0 commit comments