@@ -2,23 +2,17 @@ package resourceapply
22
33import (
44 "context"
5- errorsstdlib "errors"
6- "fmt"
75
6+ "github.com/openshift/library-go/pkg/operator/events"
7+ "github.com/openshift/library-go/pkg/operator/resource/resourcehelper"
8+ "github.com/openshift/library-go/pkg/operator/resource/resourcemerge"
89 "k8s.io/apimachinery/pkg/api/equality"
910 "k8s.io/apimachinery/pkg/api/errors"
1011 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1112 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12- "k8s.io/apimachinery/pkg/runtime"
1313 "k8s.io/apimachinery/pkg/runtime/schema"
1414 "k8s.io/client-go/dynamic"
1515 "k8s.io/klog/v2"
16- "k8s.io/utils/ptr"
17-
18- "github.com/openshift/library-go/pkg/operator/events"
19- "github.com/openshift/library-go/pkg/operator/resource/resourcehelper"
20-
21- "github.com/openshift/library-go/pkg/operator/resource/resourcemerge"
2216)
2317
2418var alertmanagerGVR = schema.GroupVersionResource {Group : "monitoring.coreos.com" , Version : "v1" , Resource : "alertmanagers" }
@@ -88,10 +82,10 @@ func ApplyUnstructuredResourceImproved(
8882 }
8983 existing , err := client .Resource (resourceGVR ).Namespace (namespace ).Get (ctx , name , metav1.GetOptions {})
9084 if errors .IsNotFound (err ) {
91- want , err := client .Resource (resourceGVR ).Namespace (namespace ).Create (ctx , required , metav1.CreateOptions {})
92- resourcehelper .ReportCreateEvent (recorder , required , err )
85+ want , errCreate := client .Resource (resourceGVR ).Namespace (namespace ).Create (ctx , required , metav1.CreateOptions {})
86+ resourcehelper .ReportCreateEvent (recorder , required , errCreate )
9387 cache .UpdateCachedResourceMetadata (required , want )
94- return want , true , err
88+ return want , true , errCreate
9589 }
9690 if err != nil {
9791 return nil , false , err
@@ -102,71 +96,42 @@ func ApplyUnstructuredResourceImproved(
10296 return existing , false , nil
10397 }
10498
105- // Ensure metadata field is present on the object.
10699 existingCopy := existing .DeepCopy ()
107- existingObjectMeta , found , err := unstructured .NestedMap (existingCopy .Object , "metadata" )
108- if err != nil {
109- return nil , false , err
110- }
111- if ! found {
112- return nil , false , errorsstdlib .New (fmt .Sprintf ("metadata not found in the existing object: %s/%s" , existing .GetNamespace (), existingCopy .GetName ()))
113- }
114- requiredObjectMeta , found , err := unstructured .NestedMap (required .Object , "metadata" )
115- if err != nil {
116- return nil , false , err
117- }
118- if ! found {
119- return nil , false , errorsstdlib .New (fmt .Sprintf ("metadata not found in the required object: %s/%s" , required .GetNamespace (), required .GetName ()))
120- }
121100
122- // Cast the metadata to the correct type.
123- var existingObjectMetaTyped , requiredObjectMetaTyped metav1.ObjectMeta
124- err = runtime .DefaultUnstructuredConverter .FromUnstructured (existingObjectMeta , & existingObjectMetaTyped )
125- if err != nil {
126- return nil , false , err
127- }
128- err = runtime .DefaultUnstructuredConverter .FromUnstructured (requiredObjectMeta , & requiredObjectMetaTyped )
101+ // Replace and/or merge certain metadata fields.
102+ didMetadataModify := false
103+ err = resourcemerge .EnsureObjectMetaForUnstructured (& didMetadataModify , existingCopy , required )
129104 if err != nil {
130105 return nil , false , err
131106 }
132107
133- // Fail-fast if the resource versions differ.
134- if requiredObjectMetaTyped .ResourceVersion != "" && existingObjectMetaTyped .ResourceVersion != requiredObjectMetaTyped .ResourceVersion {
135- err = errors .NewConflict (resourceGVR .GroupResource (), name , fmt .Errorf ("rejected to update %s %s because the object has been modified: desired/actual ResourceVersion: %v/%v" , existing .GetKind (), existing .GetName (), requiredObjectMetaTyped .ResourceVersion , existingObjectMetaTyped .ResourceVersion ))
136- return nil , false , err
137- }
138-
139- // Check if the metadata objects differ.
140- didMetadataModify := ptr .To (false )
141- resourcemerge .EnsureObjectMeta (didMetadataModify , & existingObjectMetaTyped , requiredObjectMetaTyped )
142-
143108 // Deep-check the spec objects for equality, and update the cache in either case.
144109 if defaultingFunc == nil {
145110 defaultingFunc = noDefaulting
146111 }
147112 if equalityChecker == nil {
148113 equalityChecker = equality .Semantic
149114 }
150- existingCopy , didSpecModify , err := ensureGenericSpec (required , existingCopy , defaultingFunc , equalityChecker )
115+ didSpecModify := false
116+ err = ensureGenericSpec (& didSpecModify , required , existingCopy , defaultingFunc , equalityChecker )
151117 if err != nil {
152118 return nil , false , err
153119 }
154- if ! didSpecModify && ! * didMetadataModify {
120+ if ! didSpecModify && ! didMetadataModify {
155121 // Update cache even if certain fields are not modified, in order to maintain a consistent cache based on the
156122 // resource hash. The resource hash depends on the entire metadata, not just the fields that were checked above,
157123 cache .UpdateCachedResourceMetadata (required , existingCopy )
158124 return existingCopy , false , nil
159125 }
160126
127+ // Perform update if resource exists but different from the required (desired) one.
161128 if klog .V (4 ).Enabled () {
162129 klog .Infof ("%s %q changes: %v" , resourceGVR .String (), namespace + "/" + name , JSONPatchNoError (existing , existingCopy ))
163130 }
164-
165- // Perform update if resource exists but different from the required (desired) one.
166- actual , err := client .Resource (resourceGVR ).Namespace (namespace ).Update (ctx , required , metav1.UpdateOptions {})
167- resourcehelper .ReportUpdateEvent (recorder , required , err )
168- cache .UpdateCachedResourceMetadata (required , actual )
169- return actual , true , err
131+ actual , errUpdate := client .Resource (resourceGVR ).Namespace (namespace ).Update (ctx , existingCopy , metav1.UpdateOptions {})
132+ resourcehelper .ReportUpdateEvent (recorder , existingCopy , errUpdate )
133+ cache .UpdateCachedResourceMetadata (existingCopy , actual )
134+ return actual , true , errUpdate
170135}
171136
172137// DeleteUnstructuredResource deletes the unstructured resource.
@@ -182,27 +147,27 @@ func DeleteUnstructuredResource(ctx context.Context, client dynamic.Interface, r
182147 return nil , true , nil
183148}
184149
185- func ensureGenericSpec (required , existing * unstructured.Unstructured , mimicDefaultingFn mimicDefaultingFunc , equalityChecker equalityChecker ) ( * unstructured. Unstructured , bool , error ) {
150+ func ensureGenericSpec (didSpecModify * bool , required , existing * unstructured.Unstructured , mimicDefaultingFn mimicDefaultingFunc , equalityChecker equalityChecker ) error {
186151 mimicDefaultingFn (required )
187152 requiredSpec , _ , err := unstructured .NestedMap (required .UnstructuredContent (), "spec" )
188153 if err != nil {
189- return nil , false , err
154+ return err
190155 }
191156 existingSpec , _ , err := unstructured .NestedMap (existing .UnstructuredContent (), "spec" )
192157 if err != nil {
193- return nil , false , err
158+ return err
194159 }
195160
196161 if equalityChecker .DeepEqual (existingSpec , requiredSpec ) {
197- return existing , false , nil
162+ return nil
198163 }
199164
200- existingCopy := existing .DeepCopy ()
201- if err := unstructured .SetNestedMap (existingCopy .UnstructuredContent (), requiredSpec , "spec" ); err != nil {
202- return nil , true , err
165+ if err = unstructured .SetNestedMap (existing .UnstructuredContent (), requiredSpec , "spec" ); err != nil {
166+ return err
203167 }
168+ * didSpecModify = true
204169
205- return existingCopy , true , nil
170+ return nil
206171}
207172
208173// mimicDefaultingFunc is used to set fields that are defaulted. This allows for sparse manifests to apply correctly.
0 commit comments