-
Notifications
You must be signed in to change notification settings - Fork 5
/
patch.go
180 lines (161 loc) · 6.64 KB
/
patch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package node
import (
"encoding/json"
"errors"
"os"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/yaml"
)
// Patch use the default patch type(Strategic Merge Patch) to patch node.
// Supported patch types are: "StrategicMergePatchType", "MergePatchType", "JSONPatchType".
//
// For further more Strategic Merge patch, see:
// https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#before-you-begin
// For a comparison of JSON patch and JSON merge patch, see:
// https://erosb.github.io/post/json-patch-vs-merge-patch/
func (h *Handler) Patch(original *corev1.Node, patch interface{}, patchOptions ...types.PatchType) (*corev1.Node, error) {
switch val := patch.(type) {
case string:
var err error
var patchData []byte
var jsonData []byte
if patchData, err = os.ReadFile(val); err != nil {
return nil, err
}
if jsonData, err = yaml.ToJSON(patchData); err != nil {
return nil, err
}
if len(patchOptions) != 0 && patchOptions[0] == types.JSONPatchType {
return h.jsonPatch(original, jsonData)
}
if len(patchOptions) != 0 && patchOptions[0] == types.MergePatchType {
return h.jsonMergePatch(original, jsonData)
}
return h.strategicMergePatch(original, jsonData)
case []byte:
var err error
var jsonData []byte
if jsonData, err = yaml.ToJSON(val); err != nil {
return nil, err
}
if len(patchOptions) != 0 && patchOptions[0] == types.JSONPatchType {
return h.jsonPatch(original, jsonData)
}
if len(patchOptions) != 0 && patchOptions[0] == types.MergePatchType {
return h.jsonMergePatch(original, jsonData)
}
return h.strategicMergePatch(original, jsonData)
case *corev1.Node:
return h.diffMergePatch(original, val, patchOptions...)
case corev1.Node:
return h.diffMergePatch(original, &val, patchOptions...)
case map[string]interface{}:
modified := &corev1.Node{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(val, modified); err != nil {
return nil, err
}
return h.diffMergePatch(original, modified, patchOptions...)
case *unstructured.Unstructured:
modified := &corev1.Node{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(val.UnstructuredContent(), modified); err != nil {
return nil, err
}
return h.diffMergePatch(original, modified, patchOptions...)
case unstructured.Unstructured:
modified := &corev1.Node{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(val.UnstructuredContent(), modified); err != nil {
return nil, err
}
return h.diffMergePatch(original, modified, patchOptions...)
case metav1.Object, runtime.Object:
modified, ok := patch.(*corev1.Node)
if !ok {
return nil, errors.New("patch data type is not *corev1.Node")
}
return h.diffMergePatch(original, modified, patchOptions...)
default:
return nil, ErrInvalidPatchType
}
}
// strategicMergePatch use the "Strategic Merge Patch" patch type to patch node.
//
// Notice that the patch did not replace the containers list. Instead it added
// a new Container to the list. In other words, the list in the patch was merged
// with the existing list.
//
// This is not always what happens when you use a strategic merge patch on a list.
// In some cases, the list is replaced, not merged.
//
// Note: Strategic merge patch is not supported for custom resources.
// For further more Strategic Merge patch, see:
// https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#before-you-begin
func (h *Handler) strategicMergePatch(original *corev1.Node, patchData []byte) (*corev1.Node, error) {
if len(patchData) == 0 || string(patchData) == "{}" {
return original, nil
}
return h.clientset.CoreV1().Nodes().
Patch(h.ctx, original.Name, types.StrategicMergePatchType, patchData, h.Options.PatchOptions)
}
// jsonMergePatch use the "JSON Merge Patch" patch type to patch node.
// A JSON merge patch is different from strategic merge patch, With a JSON merge patch,
// If you want to update a list, you have to specify the entire new list.
// And the new list completely replicas the existing list.
//
// For a comparison of JSON patch and JSON merge patch, see:
// https://erosb.github.io/post/json-patch-vs-merge-patch/
// For further more Json Patch see:
// https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#before-you-begin
// https://tools.ietf.org/html/rfc6902
func (h *Handler) jsonMergePatch(original *corev1.Node, patchData []byte) (*corev1.Node, error) {
if len(patchData) == 0 || string(patchData) == "{}" {
return original, nil
}
return h.clientset.CoreV1().Nodes().
Patch(h.ctx, original.Name, types.MergePatchType, patchData, h.Options.PatchOptions)
}
// jsonPatch use "JSON Patch" patch type to patch node.
//
// For a comparison of JSON patch and JSON merge patch, see:
// https://erosb.github.io/post/json-patch-vs-merge-patch/
// For further more Json Merge Patch see:
// https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#before-you-begin
// https://tools.ietf.org/html/rfc7386
func (h *Handler) jsonPatch(original *corev1.Node, patchData []byte) (*corev1.Node, error) {
return h.clientset.CoreV1().Nodes().Patch(h.ctx,
original.Name, types.JSONPatchType, patchData, h.Options.PatchOptions)
}
// diffMergePatch will tak the difference data between original and modified node object,
// and use the default patch type(Strategic Merge Patch) patch the differen node.
// You can set patchOptions to MergePatchType to use the "JSON Merge Patch" to
// patch node.
func (h *Handler) diffMergePatch(original, modified *corev1.Node, patchOptions ...types.PatchType) (*corev1.Node, error) {
var (
err error
originalJson []byte
modifiedJson []byte
patchData []byte
)
if originalJson, err = json.Marshal(original); err != nil {
return nil, err
}
if modifiedJson, err = json.Marshal(modified); err != nil {
return nil, err
}
if patchData, err = strategicpatch.CreateTwoWayMergePatch(originalJson, modifiedJson, corev1.Node{}); err != nil {
return nil, err
}
if len(patchData) == 0 || string(patchData) == "{}" {
return original, nil
}
if len(patchOptions) != 0 && patchOptions[0] == types.MergePatchType {
return h.clientset.CoreV1().Nodes().
Patch(h.ctx, original.Name, types.MergePatchType, patchData, h.Options.PatchOptions)
}
return h.clientset.CoreV1().Nodes().
Patch(h.ctx, original.Name, types.StrategicMergePatchType, patchData, h.Options.PatchOptions)
}