Merge pull request #615 from betaincao/command_override
Command and Args Overrider
This commit is contained in:
commit
f8873c840f
|
@ -43,6 +43,68 @@ spec:
|
|||
description: Overriders represents the override rules that would apply
|
||||
on resources
|
||||
properties:
|
||||
argsOverrider:
|
||||
description: ArgsOverrider represents the rules dedicated to handling
|
||||
container args
|
||||
items:
|
||||
description: CommandArgsOverrider represents the rules dedicated
|
||||
to handling command/args overrides.
|
||||
properties:
|
||||
containerName:
|
||||
description: The name of container
|
||||
type: string
|
||||
operator:
|
||||
description: Operator represents the operator which will
|
||||
apply on the command/args.
|
||||
enum:
|
||||
- add
|
||||
- remove
|
||||
type: string
|
||||
value:
|
||||
description: Value to be applied to command/args. Items
|
||||
in Value which will be appended after command/args when
|
||||
Operator is 'add'. Items in Value which match in command/args
|
||||
will be deleted when Operator is 'remove'. If Value is
|
||||
empty, then the command/args will remain the same.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- containerName
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
commandOverrider:
|
||||
description: CommandOverrider represents the rules dedicated to
|
||||
handling container command
|
||||
items:
|
||||
description: CommandArgsOverrider represents the rules dedicated
|
||||
to handling command/args overrides.
|
||||
properties:
|
||||
containerName:
|
||||
description: The name of container
|
||||
type: string
|
||||
operator:
|
||||
description: Operator represents the operator which will
|
||||
apply on the command/args.
|
||||
enum:
|
||||
- add
|
||||
- remove
|
||||
type: string
|
||||
value:
|
||||
description: Value to be applied to command/args. Items
|
||||
in Value which will be appended after command/args when
|
||||
Operator is 'add'. Items in Value which match in command/args
|
||||
will be deleted when Operator is 'remove'. If Value is
|
||||
empty, then the command/args will remain the same.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- containerName
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
imageOverrider:
|
||||
description: ImageOverrider represents the rules dedicated to
|
||||
handling image overrides.
|
||||
|
|
|
@ -43,6 +43,68 @@ spec:
|
|||
description: Overriders represents the override rules that would apply
|
||||
on resources
|
||||
properties:
|
||||
argsOverrider:
|
||||
description: ArgsOverrider represents the rules dedicated to handling
|
||||
container args
|
||||
items:
|
||||
description: CommandArgsOverrider represents the rules dedicated
|
||||
to handling command/args overrides.
|
||||
properties:
|
||||
containerName:
|
||||
description: The name of container
|
||||
type: string
|
||||
operator:
|
||||
description: Operator represents the operator which will
|
||||
apply on the command/args.
|
||||
enum:
|
||||
- add
|
||||
- remove
|
||||
type: string
|
||||
value:
|
||||
description: Value to be applied to command/args. Items
|
||||
in Value which will be appended after command/args when
|
||||
Operator is 'add'. Items in Value which match in command/args
|
||||
will be deleted when Operator is 'remove'. If Value is
|
||||
empty, then the command/args will remain the same.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- containerName
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
commandOverrider:
|
||||
description: CommandOverrider represents the rules dedicated to
|
||||
handling container command
|
||||
items:
|
||||
description: CommandArgsOverrider represents the rules dedicated
|
||||
to handling command/args overrides.
|
||||
properties:
|
||||
containerName:
|
||||
description: The name of container
|
||||
type: string
|
||||
operator:
|
||||
description: Operator represents the operator which will
|
||||
apply on the command/args.
|
||||
enum:
|
||||
- add
|
||||
- remove
|
||||
type: string
|
||||
value:
|
||||
description: Value to be applied to command/args. Items
|
||||
in Value which will be appended after command/args when
|
||||
Operator is 'add'. Items in Value which match in command/args
|
||||
will be deleted when Operator is 'remove'. If Value is
|
||||
empty, then the command/args will remain the same.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- containerName
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
imageOverrider:
|
||||
description: ImageOverrider represents the rules dedicated to
|
||||
handling image overrides.
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: policy.karmada.io/v1alpha1
|
||||
kind: OverridePolicy
|
||||
metadata:
|
||||
name: example-override
|
||||
namespace: default
|
||||
spec:
|
||||
resourceSelectors:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
targetCluster:
|
||||
clusterNames:
|
||||
- member1
|
||||
overriders:
|
||||
commandOverrider:
|
||||
- containerName: alpine
|
||||
operator: add
|
||||
value:
|
||||
- test
|
|
@ -49,6 +49,14 @@ type Overriders struct {
|
|||
// ImageOverrider represents the rules dedicated to handling image overrides.
|
||||
// +optional
|
||||
ImageOverrider []ImageOverrider `json:"imageOverrider,omitempty"`
|
||||
|
||||
// CommandOverrider represents the rules dedicated to handling container command
|
||||
// +optional
|
||||
CommandOverrider []CommandArgsOverrider `json:"commandOverrider,omitempty"`
|
||||
|
||||
// ArgsOverrider represents the rules dedicated to handling container args
|
||||
// +optional
|
||||
ArgsOverrider []CommandArgsOverrider `json:"argsOverrider,omitempty"`
|
||||
}
|
||||
|
||||
// ImageOverrider represents the rules dedicated to handling image overrides.
|
||||
|
@ -106,6 +114,25 @@ type ImagePredicate struct {
|
|||
// ImageComponent indicates the components for image.
|
||||
type ImageComponent string
|
||||
|
||||
// CommandArgsOverrider represents the rules dedicated to handling command/args overrides.
|
||||
type CommandArgsOverrider struct {
|
||||
// The name of container
|
||||
// +required
|
||||
ContainerName string `json:"containerName"`
|
||||
|
||||
// Operator represents the operator which will apply on the command/args.
|
||||
// +kubebuilder:validation:Enum=add;remove
|
||||
// +required
|
||||
Operator OverriderOperator `json:"operator"`
|
||||
|
||||
// Value to be applied to command/args.
|
||||
// Items in Value which will be appended after command/args when Operator is 'add'.
|
||||
// Items in Value which match in command/args will be deleted when Operator is 'remove'.
|
||||
// If Value is empty, then the command/args will remain the same.
|
||||
// +optional
|
||||
Value []string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// Registry is the registry component of an image with format '[registry/]repository[:tag]'.
|
||||
Registry ImageComponent = "Registry"
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
package overridemanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
||||
"github.com/karmada-io/karmada/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// CommandString command string
|
||||
CommandString = "command"
|
||||
// ArgsString args string
|
||||
ArgsString = "args"
|
||||
)
|
||||
|
||||
// buildCommandArgsPatches build JSON patches for the resource object according to override declaration.
|
||||
func buildCommandArgsPatches(target string, rawObj *unstructured.Unstructured, commandRunOverrider *policyv1alpha1.CommandArgsOverrider) ([]overrideOption, error) {
|
||||
switch rawObj.GetKind() {
|
||||
case util.PodKind:
|
||||
return buildCommandArgsPatchesWithPath(target, "spec/containers", rawObj, commandRunOverrider)
|
||||
case util.ReplicaSetKind:
|
||||
fallthrough
|
||||
case util.DeploymentKind:
|
||||
fallthrough
|
||||
case util.DaemonSetKind:
|
||||
fallthrough
|
||||
case util.StatefulSetKind:
|
||||
return buildCommandArgsPatchesWithPath(target, "spec/template/spec/containers", rawObj, commandRunOverrider)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func buildCommandArgsPatchesWithPath(target string, specContainersPath string, rawObj *unstructured.Unstructured, commandRunOverrider *policyv1alpha1.CommandArgsOverrider) ([]overrideOption, error) {
|
||||
patches := make([]overrideOption, 0)
|
||||
containers, ok, err := unstructured.NestedSlice(rawObj.Object, strings.Split(specContainersPath, pathSplit)...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieves path(%s) from rawObj, error: %v", specContainersPath, err)
|
||||
}
|
||||
if !ok || len(containers) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
klog.V(4).Infof("buildCommandArgsPatchesWithPath containers info (%+v)", containers)
|
||||
for index, container := range containers {
|
||||
if container.(map[string]interface{})["name"] == commandRunOverrider.ContainerName {
|
||||
commandArgsPath := fmt.Sprintf("/%s/%d/%s", specContainersPath, index, target)
|
||||
commandArgsValue := make([]string, 0)
|
||||
var patch overrideOption
|
||||
// if target is nil, to add new [target]
|
||||
if container.(map[string]interface{})[target] == nil {
|
||||
patch, _ = acquireAddOverrideOption(commandArgsPath, commandRunOverrider)
|
||||
} else {
|
||||
for _, val := range container.(map[string]interface{})[target].([]interface{}) {
|
||||
commandArgsValue = append(commandArgsValue, fmt.Sprintf("%s", val))
|
||||
}
|
||||
patch, _ = acquireReplaceOverrideOption(commandArgsPath, commandArgsValue, commandRunOverrider)
|
||||
}
|
||||
|
||||
klog.V(4).Infof("[buildCommandArgsPatchesWithPath] containers patch info (%+v)", patch)
|
||||
patches = append(patches, patch)
|
||||
}
|
||||
}
|
||||
return patches, nil
|
||||
}
|
||||
|
||||
func acquireAddOverrideOption(commandArgsPath string, commandOverrider *policyv1alpha1.CommandArgsOverrider) (overrideOption, error) {
|
||||
if !strings.HasPrefix(commandArgsPath, pathSplit) {
|
||||
return overrideOption{}, fmt.Errorf("internal error: [acquireCommandOverrideOption] commandRunPath should be start with / character")
|
||||
}
|
||||
newCommandArgs, err := overrideCommandArgs([]string{}, commandOverrider)
|
||||
if err != nil {
|
||||
return overrideOption{}, err
|
||||
}
|
||||
return overrideOption{
|
||||
Op: string(policyv1alpha1.OverriderOpAdd),
|
||||
Path: commandArgsPath,
|
||||
Value: newCommandArgs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func acquireReplaceOverrideOption(commandArgsPath string, commandArgsValue []string, commandOverrider *policyv1alpha1.CommandArgsOverrider) (overrideOption, error) {
|
||||
if !strings.HasPrefix(commandArgsPath, pathSplit) {
|
||||
return overrideOption{}, fmt.Errorf("internal error: [acquireCommandOverrideOption] commandRunPath should be start with / character")
|
||||
}
|
||||
|
||||
newCommandArgs, err := overrideCommandArgs(commandArgsValue, commandOverrider)
|
||||
if err != nil {
|
||||
return overrideOption{}, err
|
||||
}
|
||||
|
||||
return overrideOption{
|
||||
Op: string(policyv1alpha1.OverriderOpReplace),
|
||||
Path: commandArgsPath,
|
||||
Value: newCommandArgs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func overrideCommandArgs(curCommandArgs []string, commandArgsOverrider *policyv1alpha1.CommandArgsOverrider) ([]string, error) {
|
||||
var newCommandArgs []string
|
||||
switch commandArgsOverrider.Operator {
|
||||
case policyv1alpha1.OverriderOpAdd:
|
||||
newCommandArgs = append(curCommandArgs, commandArgsOverrider.Value...)
|
||||
case policyv1alpha1.OverriderOpRemove:
|
||||
newCommandArgs = commandArgsRemove(curCommandArgs, commandArgsOverrider.Value)
|
||||
default:
|
||||
newCommandArgs = curCommandArgs
|
||||
klog.V(4).Infof("[overrideCommandArgs], op: %s , op not supported, ignored.", policyv1alpha1.OverriderOpRemove)
|
||||
}
|
||||
return newCommandArgs, nil
|
||||
}
|
||||
|
||||
func commandArgsRemove(curCommandArgs []string, removeValues []string) []string {
|
||||
newCommandArgs := make([]string, 0, len(curCommandArgs))
|
||||
currentSet := sets.NewString(removeValues...)
|
||||
for _, val := range curCommandArgs {
|
||||
if !currentSet.Has(val) {
|
||||
newCommandArgs = append(newCommandArgs, val)
|
||||
}
|
||||
}
|
||||
return newCommandArgs
|
||||
}
|
|
@ -0,0 +1,498 @@
|
|||
package overridemanager
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
||||
)
|
||||
|
||||
func generateTestCommandDeploymentYaml() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": 1,
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "nginx",
|
||||
"name": "nginx",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}}}
|
||||
}
|
||||
|
||||
func generateTestArgsDeploymentYaml() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": 1,
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "nginx",
|
||||
"name": "nginx",
|
||||
"args": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}}}
|
||||
}
|
||||
|
||||
func generateTestCommandPodYaml() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "fictional.registry.example/imagename:v1.0.0",
|
||||
"name": "nginx",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}
|
||||
}
|
||||
|
||||
func generateTestCommandStatefulSetYaml() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "StatefulSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "web",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": 2,
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "fictional.registry.example/imagename:v1.0.0",
|
||||
"name": "nginx",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}}}
|
||||
}
|
||||
|
||||
func generateTestCommandReplicaSetYaml() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "ReplicaSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": 1,
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "fictional.registry.example/imagename:v1.0.0",
|
||||
"name": "nginx",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}}}
|
||||
}
|
||||
|
||||
func generateTestCommandDaemonSetYaml() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "ReplicaSet",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": 1,
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"image": "fictional.registry.example/imagename:v1.0.0",
|
||||
"name": "nginx",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}}}
|
||||
}
|
||||
|
||||
func generateTestCommandDeploymentYamlWithTwoContainer() *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": 1,
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "nginx",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "nginx1",
|
||||
"command": []interface{}{"nginx", "-v", "-t"},
|
||||
}}}}}}}
|
||||
}
|
||||
|
||||
func TestParseJSONPatchesByCommandOverrider(t *testing.T) {
|
||||
type args struct {
|
||||
rawObj *unstructured.Unstructured
|
||||
CommandArgsOverrider *policyv1alpha1.CommandArgsOverrider
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []overrideOption
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "CommandArgsOverrider, resource kind: Deployment, operator: add",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDeploymentYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "add",
|
||||
Value: []string{"&& echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v", "-t", "&& echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource kind: Deployment, operator: remove",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDeploymentYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "remove",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, remove value is empty, resource kind: Deployment, operator: remove",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDeploymentYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "remove",
|
||||
Value: []string{},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v", "-t"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource has more than one container",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDeploymentYamlWithTwoContainer(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "add",
|
||||
Value: []string{"echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v", "-t", "echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource has more than one container",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDeploymentYamlWithTwoContainer(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "remove",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider resource kind: Pod, operator: add",
|
||||
args: args{
|
||||
rawObj: generateTestCommandPodYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "add",
|
||||
Value: []string{"echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v", "-t", "echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource kind: StatefulSet, operator: add",
|
||||
args: args{
|
||||
rawObj: generateTestCommandStatefulSetYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "add",
|
||||
Value: []string{"echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v", "-t", "echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource kind: ReplicaSet, operator: remove",
|
||||
args: args{
|
||||
rawObj: generateTestCommandReplicaSetYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "remove",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource kind: DaemonSet, operator: remove",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDaemonSetYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "remove",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/command",
|
||||
Value: []string{"nginx", "-v"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := buildCommandArgsPatches(CommandString, tt.args.rawObj, tt.args.CommandArgsOverrider)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("buildCommandArgsPatches() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("buildCommandArgsPatches() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseJSONPatchesByArgsOverrider(t *testing.T) {
|
||||
type args struct {
|
||||
rawObj *unstructured.Unstructured
|
||||
CommandArgsOverrider *policyv1alpha1.CommandArgsOverrider
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []overrideOption
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "CommandArgsOverrider, resource kind: Deployment, operator: replace",
|
||||
args: args{
|
||||
rawObj: generateTestArgsDeploymentYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "add",
|
||||
Value: []string{"&& echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/args",
|
||||
Value: []string{"nginx", "-v", "-t", "&& echo 'hello karmada'"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource kind: Deployment, operator: replace",
|
||||
args: args{
|
||||
rawObj: generateTestArgsDeploymentYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "remove",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "replace",
|
||||
Path: "/spec/template/spec/containers/0/args",
|
||||
Value: []string{"nginx", "-v"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
}, {
|
||||
name: "CommandArgsOverrider, resource kind: Deployment, operator: add",
|
||||
args: args{
|
||||
rawObj: generateTestCommandDeploymentYaml(),
|
||||
CommandArgsOverrider: &policyv1alpha1.CommandArgsOverrider{
|
||||
ContainerName: "nginx",
|
||||
Operator: "add",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
want: []overrideOption{
|
||||
{
|
||||
Op: "add",
|
||||
Path: "/spec/template/spec/containers/0/args",
|
||||
Value: []string{"-t"},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := buildCommandArgsPatches(ArgsString, tt.args.rawObj, tt.args.CommandArgsOverrider)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("buildCommandPatches() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("buildCommandPatches() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -242,6 +242,14 @@ func applyPolicyOverriders(rawObj *unstructured.Unstructured, overriders policyv
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// patch command
|
||||
if err := applyCommandOverriders(rawObj, overriders.CommandOverrider); err != nil {
|
||||
return err
|
||||
}
|
||||
// patch args
|
||||
if err := applyArgsOverriders(rawObj, overriders.ArgsOverrider); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return applyJSONPatch(rawObj, parseJSONPatchesByPlaintext(overriders.Plaintext))
|
||||
}
|
||||
|
@ -262,6 +270,38 @@ func applyImageOverriders(rawObj *unstructured.Unstructured, imageOverriders []p
|
|||
return nil
|
||||
}
|
||||
|
||||
func applyCommandOverriders(rawObj *unstructured.Unstructured, commandOverriders []policyv1alpha1.CommandArgsOverrider) error {
|
||||
for index := range commandOverriders {
|
||||
patches, err := buildCommandArgsPatches(CommandString, rawObj, &commandOverriders[index])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Parsed JSON patches by commandOverriders(%+v): %+v", commandOverriders[index], patches)
|
||||
if err = applyJSONPatch(rawObj, patches); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyArgsOverriders(rawObj *unstructured.Unstructured, argsOverriders []policyv1alpha1.CommandArgsOverrider) error {
|
||||
for index := range argsOverriders {
|
||||
patches, err := buildCommandArgsPatches(ArgsString, rawObj, &argsOverriders[index])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Parsed JSON patches by argsOverriders(%+v): %+v", argsOverriders[index], patches)
|
||||
if err = applyJSONPatch(rawObj, patches); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseJSONPatchesByPlaintext(overriders []policyv1alpha1.PlaintextOverrider) []overrideOption {
|
||||
patches := make([]overrideOption, 0, len(overriders))
|
||||
for i := range overriders {
|
||||
|
|
Loading…
Reference in New Issue