@@ -17,7 +17,11 @@ limitations under the License.
1717package  client
1818
1919import  (
20+ 	"fmt" 
21+ 
2022	jsonpatch "github.com/evanphx/json-patch" 
23+ 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 
24+ 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 
2125	"k8s.io/apimachinery/pkg/runtime" 
2226	"k8s.io/apimachinery/pkg/types" 
2327	"k8s.io/apimachinery/pkg/util/json" 
@@ -59,8 +63,39 @@ func ConstantPatch(patchType types.PatchType, data []byte) Patch {
5963	return  RawPatch (patchType , data )
6064}
6165
66+ // MergeFromWithOptimisticLock can be used if clients want to make sure a patch 
67+ // is being applied to the latest resource version of an object. 
68+ // 
69+ // The behavior is similar to what an Update would do, without the need to send the 
70+ // whole object. Usually this method is useful if you might have multiple clients 
71+ // acting on the same object and the same API version, but with different versions of the Go structs. 
72+ // 
73+ // For example, an "older" copy of a Widget that has fields A and B, and a "newer" copy with A, B, and C. 
74+ // Sending an update using the older struct definition results in C being dropped, whereas using a patch does not. 
75+ type  MergeFromWithOptimisticLock  struct {}
76+ 
77+ // ApplyToMergeFrom applies this configuration to the given patch options. 
78+ func  (m  MergeFromWithOptimisticLock ) ApplyToMergeFrom (in  * MergeFromOptions ) {
79+ 	in .OptimisticLock  =  true 
80+ }
81+ 
82+ // MergeFromOption is some configuration that modifies options for a merge-from patch data. 
83+ type  MergeFromOption  interface  {
84+ 	// ApplyToMergeFrom applies this configuration to the given patch options. 
85+ 	ApplyToMergeFrom (* MergeFromOptions )
86+ }
87+ 
88+ // MergeFromOptions contains options to generate a merge-from patch data. 
89+ type  MergeFromOptions  struct  {
90+ 	// OptimisticLock, when true, includes `metadata.resourceVersion` into the final 
91+ 	// patch data. If the `resourceVersion` field doesn't match what's stored, 
92+ 	// the operation results in a conflict and clients will need to try again. 
93+ 	OptimisticLock  bool 
94+ }
95+ 
6296type  mergeFromPatch  struct  {
6397	from  runtime.Object 
98+ 	opts  MergeFromOptions 
6499}
65100
66101// Type implements patch. 
@@ -80,12 +115,47 @@ func (s *mergeFromPatch) Data(obj runtime.Object) ([]byte, error) {
80115		return  nil , err 
81116	}
82117
83- 	return  jsonpatch .CreateMergePatch (originalJSON , modifiedJSON )
118+ 	data , err  :=  jsonpatch .CreateMergePatch (originalJSON , modifiedJSON )
119+ 	if  err  !=  nil  {
120+ 		return  nil , err 
121+ 	}
122+ 
123+ 	if  s .opts .OptimisticLock  {
124+ 		dataMap  :=  map [string ]interface {}{}
125+ 		if  err  :=  json .Unmarshal (data , & dataMap ); err  !=  nil  {
126+ 			return  nil , err 
127+ 		}
128+ 		fromMeta , ok  :=  s .from .(metav1.Object )
129+ 		if  ! ok  {
130+ 			return  nil , fmt .Errorf ("cannot use OptimisticLock, from object %q is not a valid metav1.Object" , s .from )
131+ 		}
132+ 		resourceVersion  :=  fromMeta .GetResourceVersion ()
133+ 		if  len (resourceVersion ) ==  0  {
134+ 			return  nil , fmt .Errorf ("cannot use OptimisticLock, from object %q does not have any resource version we can use" , s .from )
135+ 		}
136+ 		u  :=  & unstructured.Unstructured {Object : dataMap }
137+ 		u .SetResourceVersion (resourceVersion )
138+ 		data , err  =  json .Marshal (u )
139+ 		if  err  !=  nil  {
140+ 			return  nil , err 
141+ 		}
142+ 	}
143+ 
144+ 	return  data , nil 
84145}
85146
86147// MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base. 
87148func  MergeFrom (obj  runtime.Object ) Patch  {
88- 	return  & mergeFromPatch {obj }
149+ 	return  & mergeFromPatch {from : obj }
150+ }
151+ 
152+ // MergeFromWithOptions creates a Patch that patches using the merge-patch strategy with the given object as base. 
153+ func  MergeFromWithOptions (obj  runtime.Object , opts  ... MergeFromOption ) Patch  {
154+ 	options  :=  & MergeFromOptions {}
155+ 	for  _ , opt  :=  range  opts  {
156+ 		opt .ApplyToMergeFrom (options )
157+ 	}
158+ 	return  & mergeFromPatch {from : obj , opts : * options }
89159}
90160
91161// mergePatch uses a raw merge strategy to patch the object. 
0 commit comments