@@ -17,7 +17,11 @@ limitations under the License.
17
17
package client
18
18
19
19
import (
20
+ "fmt"
21
+
20
22
jsonpatch "github.com/evanphx/json-patch"
23
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
21
25
"k8s.io/apimachinery/pkg/runtime"
22
26
"k8s.io/apimachinery/pkg/types"
23
27
"k8s.io/apimachinery/pkg/util/json"
@@ -59,8 +63,38 @@ func ConstantPatch(patchType types.PatchType, data []byte) Patch {
59
63
return RawPatch (patchType , data )
60
64
}
61
65
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
+ func (m MergeFromWithOptimisticLock ) ApplyToMergeFrom (in * MergeFromOptions ) {
78
+ in .OptimisticLock = true
79
+ }
80
+
81
+ // MergeFromOption is some configuration that modifies options for a merge-from patch data.
82
+ type MergeFromOption interface {
83
+ // ApplyToMergeFrom applies this configuration to the given patch options.
84
+ ApplyToMergeFrom (* MergeFromOptions )
85
+ }
86
+
87
+ // MergeFromOptions contains options to generate a merge-from patch data.
88
+ type MergeFromOptions struct {
89
+ // OptimisticLock, when true, includes `metadata.resourceVersion` into the final
90
+ // patch data. If the `resourceVersion` field doesn't match what's stored,
91
+ // the operation results in a conflict and clients will need to try again.
92
+ OptimisticLock bool
93
+ }
94
+
62
95
type mergeFromPatch struct {
63
96
from runtime.Object
97
+ opts MergeFromOptions
64
98
}
65
99
66
100
// Type implements patch.
@@ -80,12 +114,47 @@ func (s *mergeFromPatch) Data(obj runtime.Object) ([]byte, error) {
80
114
return nil , err
81
115
}
82
116
83
- return jsonpatch .CreateMergePatch (originalJSON , modifiedJSON )
117
+ data , err := jsonpatch .CreateMergePatch (originalJSON , modifiedJSON )
118
+ if err != nil {
119
+ return nil , err
120
+ }
121
+
122
+ if s .opts .OptimisticLock {
123
+ dataMap := map [string ]interface {}{}
124
+ if err := json .Unmarshal (data , & dataMap ); err != nil {
125
+ return nil , err
126
+ }
127
+ fromMeta , ok := s .from .(metav1.Object )
128
+ if ! ok {
129
+ return nil , fmt .Errorf ("cannot use OptimisticLock, from object %q is not a valid metav1.Object" , s .from )
130
+ }
131
+ resourceVersion := fromMeta .GetResourceVersion ()
132
+ if len (resourceVersion ) == 0 {
133
+ return nil , fmt .Errorf ("cannot use OptimisticLock, from object %q does not have any resource version we can use" , s .from )
134
+ }
135
+ u := & unstructured.Unstructured {Object : dataMap }
136
+ u .SetResourceVersion (resourceVersion )
137
+ data , err = json .Marshal (u )
138
+ if err != nil {
139
+ return nil , err
140
+ }
141
+ }
142
+
143
+ return data , nil
84
144
}
85
145
86
146
// MergeFrom creates a Patch that patches using the merge-patch strategy with the given object as base.
87
147
func MergeFrom (obj runtime.Object ) Patch {
88
- return & mergeFromPatch {obj }
148
+ return & mergeFromPatch {from : obj }
149
+ }
150
+
151
+ // MergeFromWithOptions creates a Patch that patches using the merge-patch strategy with the given object as base.
152
+ func MergeFromWithOptions (obj runtime.Object , opts ... MergeFromOption ) Patch {
153
+ options := & MergeFromOptions {}
154
+ for _ , opt := range opts {
155
+ opt .ApplyToMergeFrom (options )
156
+ }
157
+ return & mergeFromPatch {from : obj , opts : * options }
89
158
}
90
159
91
160
// mergePatch uses a raw merge strategy to patch the object.
0 commit comments