feat: add fieldoverrider
Signed-off-by: sophie <yl5357@columbia.edu>
This commit is contained in:
parent
f656d9a2a0
commit
6bf6f305b8
2
go.mod
2
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/emirpasic/gods v1.18.1
|
github.com/emirpasic/gods v1.18.1
|
||||||
github.com/evanphx/json-patch/v5 v5.9.0
|
github.com/evanphx/json-patch/v5 v5.9.0
|
||||||
github.com/go-co-op/gocron v1.30.1
|
github.com/go-co-op/gocron v1.30.1
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.2
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gogo/protobuf v1.3.2
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/uuid v1.5.0
|
github.com/google/uuid v1.5.0
|
||||||
|
@ -90,7 +91,6 @@ require (
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-logr/zapr v1.3.0 // indirect
|
github.com/go-logr/zapr v1.3.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
|
||||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||||
github.com/go-openapi/swag v0.22.7 // indirect
|
github.com/go-openapi/swag v0.22.7 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
|
|
|
@ -19,14 +19,18 @@ package overridemanager
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch/v5"
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
||||||
|
"github.com/go-openapi/jsonpointer"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
||||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
||||||
|
@ -280,6 +284,52 @@ func applyJSONPatch(obj *unstructured.Unstructured, overrides []overrideOption)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyRawJSONPatch applies the override on to the given raw json object.
|
||||||
|
func applyRawJSONPatch(raw []byte, overrides []overrideOption) ([]byte, error) {
|
||||||
|
jsonPatchBytes, err := json.Marshal(overrides)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
patch, err := jsonpatch.DecodePatch(jsonPatchBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return patch.Apply(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyRawYAMLPatch(raw []byte, overrides []overrideOption) ([]byte, error) {
|
||||||
|
rawJSON, err := yaml.YAMLToJSON(raw)
|
||||||
|
if err != nil {
|
||||||
|
klog.ErrorS(err, "Failed to convert yaml to json")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonPatchBytes, err := json.Marshal(overrides)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
patch, err := jsonpatch.DecodePatch(jsonPatchBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawJSON, err = patch.Apply(rawJSON)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawYAML, err := yaml.JSONToYAML(rawJSON)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to convert json to yaml, error: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawYAML, nil
|
||||||
|
}
|
||||||
|
|
||||||
// applyPolicyOverriders applies OverridePolicy/ClusterOverridePolicy overriders to target object
|
// applyPolicyOverriders applies OverridePolicy/ClusterOverridePolicy overriders to target object
|
||||||
func applyPolicyOverriders(rawObj *unstructured.Unstructured, overriders policyv1alpha1.Overriders) error {
|
func applyPolicyOverriders(rawObj *unstructured.Unstructured, overriders policyv1alpha1.Overriders) error {
|
||||||
err := applyImageOverriders(rawObj, overriders.ImageOverrider)
|
err := applyImageOverriders(rawObj, overriders.ImageOverrider)
|
||||||
|
@ -300,6 +350,9 @@ func applyPolicyOverriders(rawObj *unstructured.Unstructured, overriders policyv
|
||||||
if err := applyAnnotationsOverriders(rawObj, overriders.AnnotationsOverrider); err != nil {
|
if err := applyAnnotationsOverriders(rawObj, overriders.AnnotationsOverrider); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := applyFieldOverriders(rawObj, overriders.FieldOverrider); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return applyJSONPatch(rawObj, parseJSONPatchesByPlaintext(overriders.Plaintext))
|
return applyJSONPatch(rawObj, parseJSONPatchesByPlaintext(overriders.Plaintext))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +405,50 @@ func applyArgsOverriders(rawObj *unstructured.Unstructured, argsOverriders []pol
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyFieldOverriders(rawObj *unstructured.Unstructured, FieldOverriders []policyv1alpha1.FieldOverrider) error {
|
||||||
|
if len(FieldOverriders) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for index := range FieldOverriders {
|
||||||
|
pointer, err := jsonpointer.New(FieldOverriders[index].FieldPath)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Build jsonpointer with overrider's path err: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, kind, err := pointer.Get(rawObj.Object)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Get value by overrider's path err: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if kind != reflect.String {
|
||||||
|
errMsg := fmt.Sprintf("Get object's value by overrider's path(%s) is not string", FieldOverriders[index].FieldPath)
|
||||||
|
klog.Errorf(errMsg)
|
||||||
|
return fmt.Errorf(errMsg)
|
||||||
|
}
|
||||||
|
dataBytes := []byte(res.(string))
|
||||||
|
klog.V(4).Infof("Parsed JSON patches by FieldOverriders[%d](%+v)", index, FieldOverriders[index])
|
||||||
|
var appliedRawData []byte
|
||||||
|
if len(FieldOverriders[index].YAML) > 0 {
|
||||||
|
appliedRawData, err = applyRawYAMLPatch(dataBytes, parseYAMLPatchesByField(FieldOverriders[index].YAML))
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error applying raw JSON patch: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if len(FieldOverriders[index].JSON) > 0 {
|
||||||
|
appliedRawData, err = applyRawJSONPatch(dataBytes, parseJSONPatchesByField(FieldOverriders[index].JSON))
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Error applying raw YAML patch: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = pointer.Set(rawObj.Object, string(appliedRawData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseJSONPatchesByPlaintext(overriders []policyv1alpha1.PlaintextOverrider) []overrideOption {
|
func parseJSONPatchesByPlaintext(overriders []policyv1alpha1.PlaintextOverrider) []overrideOption {
|
||||||
patches := make([]overrideOption, 0, len(overriders))
|
patches := make([]overrideOption, 0, len(overriders))
|
||||||
for i := range overriders {
|
for i := range overriders {
|
||||||
|
@ -363,3 +460,27 @@ func parseJSONPatchesByPlaintext(overriders []policyv1alpha1.PlaintextOverrider)
|
||||||
}
|
}
|
||||||
return patches
|
return patches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseYAMLPatchesByField(overriders []policyv1alpha1.YAMLPatchOperation) []overrideOption {
|
||||||
|
patches := make([]overrideOption, 0, len(overriders))
|
||||||
|
for i := range overriders {
|
||||||
|
patches = append(patches, overrideOption{
|
||||||
|
Op: string(overriders[i].Operator),
|
||||||
|
Path: overriders[i].SubPath,
|
||||||
|
Value: overriders[i].Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return patches
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJSONPatchesByField(overriders []policyv1alpha1.JSONPatchOperation) []overrideOption {
|
||||||
|
patches := make([]overrideOption, 0, len(overriders))
|
||||||
|
for i := range overriders {
|
||||||
|
patches = append(patches, overrideOption{
|
||||||
|
Op: string(overriders[i].Operator),
|
||||||
|
Path: overriders[i].SubPath,
|
||||||
|
Value: overriders[i].Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return patches
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/test/helper"
|
"github.com/karmada-io/karmada/test/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_overrideManagerImpl_ApplyOverridePolicies(t *testing.T) {
|
func Test_overrideManagerImpl_ApplyLabelAnnotationOverriderPolicies(t *testing.T) {
|
||||||
deployment := helper.NewDeployment(metav1.NamespaceDefault, "test1")
|
deployment := helper.NewDeployment(metav1.NamespaceDefault, "test1")
|
||||||
deployment.Labels = map[string]string{
|
deployment.Labels = map[string]string{
|
||||||
"testLabel": "testLabel",
|
"testLabel": "testLabel",
|
||||||
|
@ -442,3 +442,242 @@ func TestGetMatchingOverridePolicies(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_overrideManagerImpl_ApplyFieldOverriderPolicies_YAML(t *testing.T) {
|
||||||
|
configmap := helper.NewConfigMap(metav1.NamespaceDefault, "test1", map[string]string{
|
||||||
|
"test.yaml": `
|
||||||
|
key:
|
||||||
|
key1: value
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
configmapObj, _ := utilhelper.ToUnstructured(configmap)
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
Client client.Client
|
||||||
|
EventRecorder record.EventRecorder
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
rawObj *unstructured.Unstructured
|
||||||
|
clusterName string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
wantCOP *AppliedOverrides
|
||||||
|
wantOP *AppliedOverrides
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test yaml overridePolicies",
|
||||||
|
fields: fields{
|
||||||
|
Client: fake.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(helper.NewCluster("test1"),
|
||||||
|
&policyv1alpha1.OverridePolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test1", Namespace: metav1.NamespaceDefault},
|
||||||
|
Spec: policyv1alpha1.OverrideSpec{
|
||||||
|
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "test1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
||||||
|
{
|
||||||
|
TargetCluster: &policyv1alpha1.ClusterAffinity{ClusterNames: []string{"test1"}},
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/test.yaml",
|
||||||
|
YAML: []policyv1alpha1.YAMLPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/key/key1",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`"updated_value"`)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).Build(),
|
||||||
|
EventRecorder: &record.FakeRecorder{},
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
rawObj: configmapObj,
|
||||||
|
clusterName: "test1",
|
||||||
|
},
|
||||||
|
wantCOP: nil,
|
||||||
|
wantOP: &AppliedOverrides{
|
||||||
|
AppliedItems: []OverridePolicyShadow{
|
||||||
|
{
|
||||||
|
PolicyName: "test1",
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/test.yaml",
|
||||||
|
YAML: []policyv1alpha1.YAMLPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/key/key1",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`"updated_value"`)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
o := &overrideManagerImpl{
|
||||||
|
Client: tt.fields.Client,
|
||||||
|
EventRecorder: tt.fields.EventRecorder,
|
||||||
|
}
|
||||||
|
gotCOP, gotOP, err := o.ApplyOverridePolicies(tt.args.rawObj, tt.args.clusterName)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ApplyOverridePolicies() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotCOP, tt.wantCOP) {
|
||||||
|
t.Errorf("ApplyOverridePolicies() gotCOP = %v, wantCOP %v", gotCOP, tt.wantCOP)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotOP, tt.wantOP) {
|
||||||
|
t.Errorf("ApplyOverridePolicies() gotOP = %v, wantOP %v", gotOP, tt.wantOP)
|
||||||
|
}
|
||||||
|
wantData := map[string]interface{}{
|
||||||
|
"test.yaml": `key:
|
||||||
|
key1: updated_value
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.args.rawObj.Object["data"], wantData) {
|
||||||
|
t.Errorf("ApplyOverridePolicies() gotData = %v, wantData %v", tt.args.rawObj.Object["data"], wantData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_overrideManagerImpl_ApplyJSONOverridePolicies_JSON(t *testing.T) {
|
||||||
|
configmap := helper.NewConfigMap(metav1.NamespaceDefault, "test1", map[string]string{
|
||||||
|
"test.json": `{"key":{"key1":"value"}}`,
|
||||||
|
})
|
||||||
|
configmapObj, _ := utilhelper.ToUnstructured(configmap)
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
Client client.Client
|
||||||
|
EventRecorder record.EventRecorder
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
rawObj *unstructured.Unstructured
|
||||||
|
clusterName string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
wantCOP *AppliedOverrides
|
||||||
|
wantOP *AppliedOverrides
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test yaml overridePolicies",
|
||||||
|
fields: fields{
|
||||||
|
Client: fake.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(helper.NewCluster("test1"),
|
||||||
|
&policyv1alpha1.OverridePolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test1", Namespace: metav1.NamespaceDefault},
|
||||||
|
Spec: policyv1alpha1.OverrideSpec{
|
||||||
|
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
Namespace: "default",
|
||||||
|
Name: "test1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
||||||
|
{
|
||||||
|
TargetCluster: &policyv1alpha1.ClusterAffinity{ClusterNames: []string{"test1"}},
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/test.json",
|
||||||
|
JSON: []policyv1alpha1.JSONPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/key/key1",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`"updated_value"`)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).Build(),
|
||||||
|
EventRecorder: &record.FakeRecorder{},
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
rawObj: configmapObj,
|
||||||
|
clusterName: "test1",
|
||||||
|
},
|
||||||
|
wantCOP: nil,
|
||||||
|
wantOP: &AppliedOverrides{
|
||||||
|
AppliedItems: []OverridePolicyShadow{
|
||||||
|
{
|
||||||
|
PolicyName: "test1",
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/test.json",
|
||||||
|
JSON: []policyv1alpha1.JSONPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/key/key1",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`"updated_value"`)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
o := &overrideManagerImpl{
|
||||||
|
Client: tt.fields.Client,
|
||||||
|
EventRecorder: tt.fields.EventRecorder,
|
||||||
|
}
|
||||||
|
gotCOP, gotOP, err := o.ApplyOverridePolicies(tt.args.rawObj, tt.args.clusterName)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ApplyOverridePolicies() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotCOP, tt.wantCOP) {
|
||||||
|
t.Errorf("ApplyOverridePolicies() gotCOP = %v, wantCOP %v", gotCOP, tt.wantCOP)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(gotOP, tt.wantOP) {
|
||||||
|
t.Errorf("ApplyOverridePolicies() gotOP = %v, wantOP %v", gotOP, tt.wantOP)
|
||||||
|
}
|
||||||
|
wantData := map[string]interface{}{
|
||||||
|
"test.json": `{"key":{"key1":"updated_value"}}`,
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.args.rawObj.Object["data"], wantData) {
|
||||||
|
t.Errorf("ApplyOverridePolicies() gotData = %v, wantData %v", tt.args.rawObj.Object["data"], wantData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ package validation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
"github.com/go-openapi/jsonpointer"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||||
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||||
|
@ -304,13 +304,53 @@ func ValidateOverrideRules(overrideRules []policyv1alpha1.RuleWithCluster, fldPa
|
||||||
// validates predicate path.
|
// validates predicate path.
|
||||||
for imageIndex, image := range rule.Overriders.ImageOverrider {
|
for imageIndex, image := range rule.Overriders.ImageOverrider {
|
||||||
imagePath := rulePath.Child("overriders").Child("imageOverrider").Index(imageIndex)
|
imagePath := rulePath.Child("overriders").Child("imageOverrider").Index(imageIndex)
|
||||||
if image.Predicate != nil && !strings.HasPrefix(image.Predicate.Path, "/") {
|
if image.Predicate != nil {
|
||||||
allErrs = append(allErrs, field.Invalid(imagePath.Child("predicate").Child("path"), image.Predicate.Path, "path should be start with / character"))
|
if _, err := jsonpointer.New(image.Predicate.Path); err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(imagePath.Child("predicate").Child("path"), image.Predicate.Path, err.Error()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for fieldIndex, fieldOverrider := range rule.Overriders.FieldOverrider {
|
||||||
|
fieldPath := rulePath.Child("overriders").Child("fieldOverrider").Index(fieldIndex)
|
||||||
|
// validates that either YAML or JSON is selected for each field overrider.
|
||||||
|
if len(fieldOverrider.YAML) > 0 && len(fieldOverrider.JSON) > 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fieldPath, fieldOverrider, "FieldOverrider has both YAML and JSON set. Only one is allowed"))
|
||||||
|
}
|
||||||
|
// validates the field path.
|
||||||
|
if _, err := jsonpointer.New(fieldOverrider.FieldPath); err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fieldPath.Child("fieldPath"), fieldOverrider.FieldPath, err.Error()))
|
||||||
|
}
|
||||||
|
// validates the JSON patch operations sub path.
|
||||||
|
allErrs = append(allErrs, validateJSONPatchSubPaths(fieldOverrider.JSON, fieldPath.Child("json"))...)
|
||||||
|
// validates the YAML patch operations sub path.
|
||||||
|
allErrs = append(allErrs, validateYAMLPatchSubPaths(fieldOverrider.YAML, fieldPath.Child("yaml"))...)
|
||||||
|
}
|
||||||
|
|
||||||
// validates the targetCluster.
|
// validates the targetCluster.
|
||||||
allErrs = append(allErrs, ValidateClusterAffinity(rule.TargetCluster, rulePath.Child("targetCluster"))...)
|
allErrs = append(allErrs, ValidateClusterAffinity(rule.TargetCluster, rulePath.Child("targetCluster"))...)
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateJSONPatchSubPaths(patches []policyv1alpha1.JSONPatchOperation, fieldPath *field.Path) field.ErrorList {
|
||||||
|
var allErrs field.ErrorList
|
||||||
|
for index, patch := range patches {
|
||||||
|
patchPath := fieldPath.Index(index)
|
||||||
|
if _, err := jsonpointer.New(patch.SubPath); err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(patchPath.Child("subPath"), patch.SubPath, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateYAMLPatchSubPaths(patches []policyv1alpha1.YAMLPatchOperation, fieldPath *field.Path) field.ErrorList {
|
||||||
|
var allErrs field.ErrorList
|
||||||
|
for index, patch := range patches {
|
||||||
|
patchPath := fieldPath.Index(index)
|
||||||
|
if _, err := jsonpointer.New(patch.SubPath); err != nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(patchPath.Child("subPath"), patch.SubPath, err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||||
|
|
||||||
|
@ -154,6 +155,147 @@ func TestValidatingAdmission_Handle(t *testing.T) {
|
||||||
Message: "",
|
Message: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Handle_FieldOverrider_ContainsBothYAMLAndJSON_DeniesAdmission",
|
||||||
|
decoder: &fakeValidationDecoder{
|
||||||
|
obj: &policyv1alpha1.OverridePolicy{
|
||||||
|
Spec: policyv1alpha1.OverrideSpec{
|
||||||
|
ResourceSelectors: []policyv1alpha1.ResourceSelector{
|
||||||
|
{APIVersion: "test-apiversion", Kind: "test"},
|
||||||
|
},
|
||||||
|
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
||||||
|
{
|
||||||
|
TargetCluster: &policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: []string{"member1"},
|
||||||
|
},
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/config",
|
||||||
|
JSON: []policyv1alpha1.JSONPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/db-config",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`{"db": "new"}`)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
YAML: []policyv1alpha1.YAMLPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/db-config",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte("db: new")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: admission.Request{},
|
||||||
|
want: TestResponse{
|
||||||
|
Type: Denied,
|
||||||
|
Message: "FieldOverrider has both YAML and JSON set. Only one is allowed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Handle_InvalidFieldPathInYAML_DeniesAdmission",
|
||||||
|
decoder: &fakeValidationDecoder{
|
||||||
|
obj: &policyv1alpha1.OverridePolicy{
|
||||||
|
Spec: policyv1alpha1.OverrideSpec{
|
||||||
|
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
||||||
|
{
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "invalidPath",
|
||||||
|
YAML: []policyv1alpha1.YAMLPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/db-config",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte("db: new")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: admission.Request{},
|
||||||
|
want: TestResponse{
|
||||||
|
Type: Denied,
|
||||||
|
Message: "spec.overrideRules[0].overriders.fieldOverrider[0].fieldPath: Invalid value: \"invalidPath\": JSON pointer must be empty or start with a \"/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Handle_InvalidJSONSubPath_DeniesAdmission",
|
||||||
|
decoder: &fakeValidationDecoder{
|
||||||
|
obj: &policyv1alpha1.OverridePolicy{
|
||||||
|
Spec: policyv1alpha1.OverrideSpec{
|
||||||
|
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
||||||
|
{
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/config",
|
||||||
|
JSON: []policyv1alpha1.JSONPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "invalidSubPath",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`{"db": "new"}`)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: admission.Request{},
|
||||||
|
want: TestResponse{
|
||||||
|
Type: Denied,
|
||||||
|
Message: "spec.overrideRules[0].overriders.fieldOverrider[0].json[0].subPath: Invalid value: \"invalidSubPath\": JSON pointer must be empty or start with a \"/",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Handle_InvalidYAMLSubPath_DeniesAdmission",
|
||||||
|
decoder: &fakeValidationDecoder{
|
||||||
|
obj: &policyv1alpha1.OverridePolicy{
|
||||||
|
Spec: policyv1alpha1.OverrideSpec{
|
||||||
|
OverrideRules: []policyv1alpha1.RuleWithCluster{
|
||||||
|
{
|
||||||
|
Overriders: policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/config",
|
||||||
|
YAML: []policyv1alpha1.YAMLPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "invalidSubPath",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte("db: new")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req: admission.Request{},
|
||||||
|
want: TestResponse{
|
||||||
|
Type: Denied,
|
||||||
|
Message: "spec.overrideRules[0].overriders.fieldOverrider[0].yaml[0].subPath: Invalid value: \"invalidSubPath\": JSON pointer must be empty or start with a \"/",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
| Check if the OverridePolicy will update the deployment's image value | deployment imageOverride testing | |
|
| Check if the OverridePolicy will update the deployment's image value | deployment imageOverride testing | |
|
||||||
| Check if the OverridePolicy will update the pod's image value | pod imageOverride testing | |
|
| Check if the OverridePolicy will update the pod's image value | pod imageOverride testing | |
|
||||||
| Check if the OverridePolicy will update the specific image value | deployment imageOverride testing | |
|
| Check if the OverridePolicy will update the specific image value | deployment imageOverride testing | |
|
||||||
|
| Check if the OverridePolicy will update the value inside JSON | deployment fieldOverride testing | |
|
||||||
|
| Check if the OverridePolicy will update the value inside YAML | deployment fieldOverride testing | |
|
||||||
|
|
||||||
#### OverridePolicy with nil resourceSelector testing
|
#### OverridePolicy with nil resourceSelector testing
|
||||||
| Test Case | E2E Describe Text | Comments |
|
| Test Case | E2E Describe Text | Comments |
|
||||||
|
|
|
@ -17,9 +17,13 @@ limitations under the License.
|
||||||
package e2e
|
package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/rand"
|
"k8s.io/apimachinery/pkg/util/rand"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
|
||||||
|
@ -425,6 +429,221 @@ var _ = ginkgo.Describe("[OverridePolicy] apply overriders testing", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ginkgo.Context("[FieldOverrider] apply field overrider testing to update JSON values in ConfigMap", func() {
|
||||||
|
var configMapNamespace, configMapName string
|
||||||
|
var configMap *corev1.ConfigMap
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
configMapNamespace = testNamespace
|
||||||
|
configMapName = configMapNamePrefix + rand.String(RandomStrLength)
|
||||||
|
propagationPolicyNamespace = testNamespace
|
||||||
|
propagationPolicyName = configMapName
|
||||||
|
overridePolicyNamespace = testNamespace
|
||||||
|
overridePolicyName = configMapName
|
||||||
|
|
||||||
|
configMapData := map[string]string{
|
||||||
|
"deploy.json": fmt.Sprintf(`{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": {
|
||||||
|
"name": "nginx-deploy",
|
||||||
|
"namespace": "%s"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"replicas": 3,
|
||||||
|
"selector": {
|
||||||
|
"matchLabels": {
|
||||||
|
"app": "nginx"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"template": {
|
||||||
|
"metadata": {
|
||||||
|
"labels": {
|
||||||
|
"app": "nginx"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "nginx",
|
||||||
|
"image": "nginx:1.19.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`, configMapNamespace),
|
||||||
|
}
|
||||||
|
|
||||||
|
configMap = helper.NewConfigMap(configMapNamespace, configMapName, configMapData)
|
||||||
|
propagationPolicy = helper.NewPropagationPolicy(propagationPolicyNamespace, propagationPolicyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: configMap.APIVersion,
|
||||||
|
Kind: configMap.Kind,
|
||||||
|
Name: configMap.Name,
|
||||||
|
},
|
||||||
|
}, policyv1alpha1.Placement{
|
||||||
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: framework.ClusterNames(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
overridePolicy = helper.NewOverridePolicy(overridePolicyNamespace, overridePolicyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: configMap.APIVersion,
|
||||||
|
Kind: configMap.Kind,
|
||||||
|
Name: configMap.Name,
|
||||||
|
},
|
||||||
|
}, policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: framework.ClusterNames(),
|
||||||
|
}, policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/deploy.json",
|
||||||
|
JSON: []policyv1alpha1.JSONPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/spec/replicas",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`5`)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SubPath: "/spec/template/spec/containers/-",
|
||||||
|
Operator: policyv1alpha1.OverriderOpAdd,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`{"name": "nginx-helper", "image": "nginx:1.19.1"}`)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SubPath: "/spec/template/spec/containers/0/image",
|
||||||
|
Operator: policyv1alpha1.OverriderOpRemove,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
framework.CreatePropagationPolicy(karmadaClient, propagationPolicy)
|
||||||
|
framework.CreateOverridePolicy(karmadaClient, overridePolicy)
|
||||||
|
framework.CreateConfigMap(kubeClient, configMap)
|
||||||
|
ginkgo.DeferCleanup(func() {
|
||||||
|
framework.RemovePropagationPolicy(karmadaClient, propagationPolicy.Namespace, propagationPolicy.Name)
|
||||||
|
framework.RemoveOverridePolicy(karmadaClient, overridePolicy.Namespace, overridePolicy.Name)
|
||||||
|
framework.RemoveConfigMap(kubeClient, configMap.Namespace, configMap.Name)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("should override JSON field in ConfigMap", func() {
|
||||||
|
klog.Infof("check if configMap present on member clusters has the correct JSON field value.")
|
||||||
|
framework.WaitConfigMapPresentOnClustersFitWith(framework.ClusterNames(), configMap.Namespace, configMap.Name,
|
||||||
|
func(cm *corev1.ConfigMap) bool {
|
||||||
|
return strings.Contains(cm.Data["deploy.json"], `"replicas":5`) &&
|
||||||
|
strings.Contains(cm.Data["deploy.json"], `"name":"nginx-helper"`) &&
|
||||||
|
!strings.Contains(cm.Data["deploy.json"], `"image":"nginx:1.19.0"`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.Context("[FieldOverrider] apply field overrider testing to update YAML values in ConfigMap", func() {
|
||||||
|
var configMapNamespace, configMapName string
|
||||||
|
var configMap *corev1.ConfigMap
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
configMapNamespace = testNamespace
|
||||||
|
configMapName = configMapNamePrefix + rand.String(RandomStrLength)
|
||||||
|
propagationPolicyNamespace = testNamespace
|
||||||
|
propagationPolicyName = configMapName
|
||||||
|
overridePolicyNamespace = testNamespace
|
||||||
|
overridePolicyName = configMapName
|
||||||
|
|
||||||
|
// Define the ConfigMap data
|
||||||
|
configMapData := map[string]string{
|
||||||
|
"nginx.yaml": `
|
||||||
|
server:
|
||||||
|
listen: 80
|
||||||
|
server_name: localhost
|
||||||
|
location /:
|
||||||
|
root: /usr/share/nginx/html
|
||||||
|
index:
|
||||||
|
- index.html
|
||||||
|
- index.htm
|
||||||
|
error_page:
|
||||||
|
- code: 500
|
||||||
|
- code: 502
|
||||||
|
- code: 503
|
||||||
|
- code: 504
|
||||||
|
location /50x.html:
|
||||||
|
root: /usr/share/nginx/html
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
configMap = helper.NewConfigMap(configMapNamespace, configMapName, configMapData)
|
||||||
|
propagationPolicy = helper.NewPropagationPolicy(propagationPolicyNamespace, propagationPolicyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: configMap.APIVersion,
|
||||||
|
Kind: configMap.Kind,
|
||||||
|
Name: configMap.Name,
|
||||||
|
},
|
||||||
|
}, policyv1alpha1.Placement{
|
||||||
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: framework.ClusterNames(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
overridePolicy = helper.NewOverridePolicy(overridePolicyNamespace, overridePolicyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: configMap.APIVersion,
|
||||||
|
Kind: configMap.Kind,
|
||||||
|
Name: configMap.Name,
|
||||||
|
},
|
||||||
|
}, policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: framework.ClusterNames(),
|
||||||
|
}, policyv1alpha1.Overriders{
|
||||||
|
FieldOverrider: []policyv1alpha1.FieldOverrider{
|
||||||
|
{
|
||||||
|
FieldPath: "/data/nginx.yaml",
|
||||||
|
YAML: []policyv1alpha1.YAMLPatchOperation{
|
||||||
|
{
|
||||||
|
SubPath: "/server/location ~1/root",
|
||||||
|
Operator: policyv1alpha1.OverriderOpReplace,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`"/var/www/html"`)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SubPath: "/server/error_page/-",
|
||||||
|
Operator: policyv1alpha1.OverriderOpAdd,
|
||||||
|
Value: apiextensionsv1.JSON{Raw: []byte(`{"code": 400}`)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SubPath: "/server/location ~1/index",
|
||||||
|
Operator: policyv1alpha1.OverriderOpRemove,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
framework.CreatePropagationPolicy(karmadaClient, propagationPolicy)
|
||||||
|
framework.CreateOverridePolicy(karmadaClient, overridePolicy)
|
||||||
|
framework.CreateConfigMap(kubeClient, configMap)
|
||||||
|
ginkgo.DeferCleanup(func() {
|
||||||
|
framework.RemovePropagationPolicy(karmadaClient, propagationPolicy.Namespace, propagationPolicy.Name)
|
||||||
|
framework.RemoveOverridePolicy(karmadaClient, overridePolicy.Namespace, overridePolicy.Name)
|
||||||
|
framework.RemoveConfigMap(kubeClient, configMap.Namespace, configMap.Name)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("should override YAML field in ConfigMap", func() {
|
||||||
|
klog.Infof("check if configMap present on member clusters has the correct YAML field value.")
|
||||||
|
framework.WaitConfigMapPresentOnClustersFitWith(framework.ClusterNames(), configMap.Namespace, configMap.Name,
|
||||||
|
func(cm *corev1.ConfigMap) bool {
|
||||||
|
return strings.Contains(cm.Data["nginx.yaml"], "root: /var/www/html") &&
|
||||||
|
strings.Contains(cm.Data["nginx.yaml"], "code: 400") &&
|
||||||
|
!strings.Contains(cm.Data["nginx.yaml"], "- index.html")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = framework.SerialDescribe("OverridePolicy with nil resourceSelector testing", func() {
|
var _ = framework.SerialDescribe("OverridePolicy with nil resourceSelector testing", func() {
|
||||||
|
|
Loading…
Reference in New Issue