parent
5932e263c9
commit
65082f33a4
|
@ -1,6 +1,7 @@
|
||||||
package resourcemodifiers
|
package resourcemodifiers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
@ -10,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONMergePatch struct {
|
type JSONMergePatch struct {
|
||||||
PatchBytes []byte `json:"patchBytes,omitempty"`
|
PatchData json.RawMessage `json:"patchData,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JSONMergePatcher struct {
|
type JSONMergePatcher struct {
|
||||||
|
@ -24,14 +25,14 @@ func (p *JSONMergePatcher) Patch(u *unstructured.Unstructured, _ logrus.FieldLog
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, patch := range p.patches {
|
for _, patch := range p.patches {
|
||||||
patchBytes, err := yaml.YAMLToJSON(patch.PatchBytes)
|
patchBytes, err := yaml.YAMLToJSON(patch.PatchData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error in converting YAML to JSON %s", err)
|
return nil, fmt.Errorf("error in converting YAML to JSON %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
objBytes, err = jsonpatch.MergePatch(objBytes, patchBytes)
|
objBytes, err = jsonpatch.MergePatch(objBytes, patchBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error in applying JSON Patch %s", err.Error())
|
return nil, fmt.Errorf("error in applying JSON Patch: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ func unmarshalResourceModifiers(yamlData []byte) (*ResourceModifiers, error) {
|
||||||
resModifiers := &ResourceModifiers{}
|
resModifiers := &ResourceModifiers{}
|
||||||
err := yaml.UnmarshalStrict(yamlData, resModifiers)
|
err := yaml.UnmarshalStrict(yamlData, resModifiers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to decode yaml data into resource modifiers %v", err)
|
return nil, fmt.Errorf("failed to decode yaml data into resource modifiers, err: %s", err)
|
||||||
}
|
}
|
||||||
return resModifiers, nil
|
return resModifiers, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,64 @@ func TestGetResourceModifiersFromConfig(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cm5 := &v1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-configmap",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"sub.yml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: pods\n namespaces:\n - ns1\n mergePatches:\n - patchData:\n metadata:\n annotations:\n foo: null",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rules5 := &ResourceModifiers{
|
||||||
|
Version: "v1",
|
||||||
|
ResourceModifierRules: []ResourceModifierRule{
|
||||||
|
{
|
||||||
|
Conditions: Conditions{
|
||||||
|
GroupResource: "pods",
|
||||||
|
Namespaces: []string{
|
||||||
|
"ns1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MergePatches: []JSONMergePatch{
|
||||||
|
{
|
||||||
|
PatchData: []byte(`{"metadata":{"annotations":{"foo":null}}}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cm6 := &v1.ConfigMap{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-configmap",
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"sub.yml": "version: v1\nresourceModifierRules:\n- conditions:\n groupResource: pods\n namespaces:\n - ns1\n strategicPatches:\n - patchData:\n spec:\n containers:\n - name: nginx\n image: repo2/nginx",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rules6 := &ResourceModifiers{
|
||||||
|
Version: "v1",
|
||||||
|
ResourceModifierRules: []ResourceModifierRule{
|
||||||
|
{
|
||||||
|
Conditions: Conditions{
|
||||||
|
GroupResource: "pods",
|
||||||
|
Namespaces: []string{
|
||||||
|
"ns1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StrategicPatches: []StrategicMergePatch{
|
||||||
|
{
|
||||||
|
PatchData: []byte(`{"spec":{"containers":[{"image":"repo2/nginx","name":"nginx"}]}}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
type args struct {
|
type args struct {
|
||||||
cm *v1.ConfigMap
|
cm *v1.ConfigMap
|
||||||
}
|
}
|
||||||
|
@ -169,6 +227,22 @@ func TestGetResourceModifiersFromConfig(t *testing.T) {
|
||||||
want: nil,
|
want: nil,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "complex payload with json merge patch",
|
||||||
|
args: args{
|
||||||
|
cm: cm5,
|
||||||
|
},
|
||||||
|
want: rules5,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "complex payload with strategic merge patch",
|
||||||
|
args: args{
|
||||||
|
cm: cm6,
|
||||||
|
},
|
||||||
|
want: rules6,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
@ -178,7 +252,7 @@ func TestGetResourceModifiersFromConfig(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("GetResourceModifiersFromConfig() = %v, want %v", got, tt.want)
|
t.Errorf("GetResourceModifiersFromConfig() = %#v, want %#v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -910,7 +984,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_StrategicMergePatch(t *tes
|
||||||
},
|
},
|
||||||
StrategicPatches: []StrategicMergePatch{
|
StrategicPatches: []StrategicMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"spec":{"containers":[{"name":"nginx","image":"nginx1"}]}}`),
|
PatchData: []byte(`{"spec":{"containers":[{"name":"nginx","image":"nginx1"}]}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -933,7 +1007,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_StrategicMergePatch(t *tes
|
||||||
},
|
},
|
||||||
StrategicPatches: []StrategicMergePatch{
|
StrategicPatches: []StrategicMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`spec:
|
PatchData: []byte(`spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: nginx1`),
|
image: nginx1`),
|
||||||
|
@ -959,7 +1033,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_StrategicMergePatch(t *tes
|
||||||
},
|
},
|
||||||
StrategicPatches: []StrategicMergePatch{
|
StrategicPatches: []StrategicMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"spec":{"volumes":[{"nfs":null,"name":"vol1","persistentVolumeClaim":{"claimName":"pvc1"}}]}}`),
|
PatchData: []byte(`{"spec":{"volumes":[{"nfs":null,"name":"vol1","persistentVolumeClaim":{"claimName":"pvc1"}}]}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -982,7 +1056,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_StrategicMergePatch(t *tes
|
||||||
},
|
},
|
||||||
StrategicPatches: []StrategicMergePatch{
|
StrategicPatches: []StrategicMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"spec":{"volumes":[{"$retainKeys":["name","persistentVolumeClaim"],"name":"vol1","persistentVolumeClaim":{"claimName":"pvc1"}}]}}`),
|
PatchData: []byte(`{"spec":{"volumes":[{"$retainKeys":["name","persistentVolumeClaim"],"name":"vol1","persistentVolumeClaim":{"claimName":"pvc1"}}]}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1005,7 +1079,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_StrategicMergePatch(t *tes
|
||||||
},
|
},
|
||||||
StrategicPatches: []StrategicMergePatch{
|
StrategicPatches: []StrategicMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"spec":{"$setElementOrder/ports":[{"port":8001},{"port":9000},{"port":8002}],"ports":[{"name":"fake","port":9000,"protocol":"TCP","targetPort":9000},{"$patch":"delete","port":8000}]}}`),
|
PatchData: []byte(`{"spec":{"$setElementOrder/ports":[{"port":8001},{"port":9000},{"port":8002}],"ports":[{"name":"fake","port":9000,"protocol":"TCP","targetPort":9000},{"$patch":"delete","port":8000}]}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1062,7 +1136,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_JSONMergePatch(t *testing.
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1085,7 +1159,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_JSONMergePatch(t *testing.
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`metadata:
|
PatchData: []byte(`metadata:
|
||||||
labels:
|
labels:
|
||||||
a: c`),
|
a: c`),
|
||||||
},
|
},
|
||||||
|
@ -1110,7 +1184,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_JSONMergePatch(t *testing.
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":null}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":null}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1133,7 +1207,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_JSONMergePatch(t *testing.
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":"b"}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":"b"}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1156,7 +1230,7 @@ func TestResourceModifiers_ApplyResourceModifierRules_JSONMergePatch(t *testing.
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":null}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":null}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1209,7 +1283,7 @@ func TestResourceModifiers_wildcard_in_GroupResource(t *testing.T) {
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1232,7 +1306,7 @@ func TestResourceModifiers_wildcard_in_GroupResource(t *testing.T) {
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1291,7 +1365,7 @@ func TestResourceModifiers_conditional_patches(t *testing.T) {
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1320,7 +1394,7 @@ func TestResourceModifiers_conditional_patches(t *testing.T) {
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":"c"}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -132,7 +132,7 @@ func TestResourceModifiers_Validate(t *testing.T) {
|
||||||
},
|
},
|
||||||
MergePatches: []JSONMergePatch{
|
MergePatches: []JSONMergePatch{
|
||||||
{
|
{
|
||||||
PatchBytes: []byte(`{"metadata":{"labels":{"a":null}}}`),
|
PatchData: []byte(`{"metadata":{"labels":{"a":null}}}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package resourcemodifiers
|
package resourcemodifiers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -13,12 +14,12 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/mergepatch"
|
"k8s.io/apimachinery/pkg/util/mergepatch"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
"sigs.k8s.io/json"
|
kubejson "sigs.k8s.io/json"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StrategicMergePatch struct {
|
type StrategicMergePatch struct {
|
||||||
PatchBytes []byte `json:"patchBytes,omitempty"`
|
PatchData json.RawMessage `json:"patchData,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StrategicMergePatcher struct {
|
type StrategicMergePatcher struct {
|
||||||
|
@ -36,7 +37,7 @@ func (p *StrategicMergePatcher) Patch(u *unstructured.Unstructured, _ logrus.Fie
|
||||||
origin := u.DeepCopy()
|
origin := u.DeepCopy()
|
||||||
updated := u.DeepCopy()
|
updated := u.DeepCopy()
|
||||||
for _, patch := range p.patches {
|
for _, patch := range p.patches {
|
||||||
patchBytes, err := yaml.YAMLToJSON(patch.PatchBytes)
|
patchBytes, err := yaml.YAMLToJSON(patch.PatchData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error in converting YAML to JSON %s", err)
|
return nil, fmt.Errorf("error in converting YAML to JSON %s", err)
|
||||||
}
|
}
|
||||||
|
@ -72,12 +73,12 @@ func strategicPatchObject(
|
||||||
patchMap := make(map[string]interface{})
|
patchMap := make(map[string]interface{})
|
||||||
var strictErrs []error
|
var strictErrs []error
|
||||||
if validationDirective == metav1.FieldValidationWarn || validationDirective == metav1.FieldValidationStrict {
|
if validationDirective == metav1.FieldValidationWarn || validationDirective == metav1.FieldValidationStrict {
|
||||||
strictErrs, err = json.UnmarshalStrict(patchBytes, &patchMap)
|
strictErrs, err = kubejson.UnmarshalStrict(patchBytes, &patchMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return apierrors.NewBadRequest(err.Error())
|
return apierrors.NewBadRequest(err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = json.UnmarshalCaseSensitivePreserveInts(patchBytes, &patchMap); err != nil {
|
if err = kubejson.UnmarshalCaseSensitivePreserveInts(patchBytes, &patchMap); err != nil {
|
||||||
return apierrors.NewBadRequest(err.Error())
|
return apierrors.NewBadRequest(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue