Extended replacement plugin with Targets field

The user can use either `target` or `targets` that is list of
several `target` objects.

Change-Id: I38e457842e1250a2d048f93ba2aac9258758d163
This commit is contained in:
Alexey Odinokov 2021-04-28 05:15:04 +00:00
parent 6543f63ebf
commit 10b0385691
4 changed files with 277 additions and 33 deletions

View File

@ -19,6 +19,33 @@ import (
"sigs.k8s.io/kustomize/api/types"
)
// These types have been changed in kustomize 4.0.5.
// Copying the version from 3.9.2
// Replacement defines how to perform a substitution
// where it is from and where it is to.
type Replacement struct {
Source *ReplSource `json:"source" yaml:"source"`
Target *ReplTarget `json:"target" yaml:"target"`
Targets []*ReplTarget `json:"targets" yaml:"targets"`
}
// ReplSource defines where a substitution is from
// It can from two different kinds of sources
// - from a field of one resource
// - from a string
type ReplSource struct {
ObjRef *types.Target `json:"objref,omitempty" yaml:"objref,omitempty"`
FieldRef string `json:"fieldref,omitempty" yaml:"fiedldref,omitempty"`
Value string `json:"value,omitempty" yaml:"value,omitempty"`
}
// ReplTarget defines where a substitution is to.
type ReplTarget struct {
ObjRef *types.Selector `json:"objref,omitempty" yaml:"objref,omitempty"`
FieldRefs []string `json:"fieldrefs,omitempty" yaml:"fieldrefs,omitempty"`
}
// +kubebuilder:object:root=true
// ReplacementTransformer plugin configuration for airship document model
@ -27,30 +54,5 @@ type ReplacementTransformer struct {
metav1.ObjectMeta `json:"metadata,omitempty"`
// Replacements list of source and target field to do a replacement
Replacements []types.Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"`
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplacementTransformer) DeepCopyInto(out *ReplacementTransformer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.Replacements != nil {
out.Replacements = make([]types.Replacement, len(in.Replacements))
for i, repl := range in.Replacements {
out.Replacements[i] = types.Replacement{
Source: &types.ReplSource{
ObjRef: &types.Target{},
FieldRef: repl.Source.FieldRef,
Value: repl.Source.Value,
},
Target: &types.ReplTarget{
ObjRef: &types.Selector{},
FieldRefs: repl.Target.FieldRefs,
},
}
*(out.Replacements[i].Source.ObjRef) = *(in.Replacements[i].Source.ObjRef)
*(out.Replacements[i].Target.ObjRef) = *(in.Replacements[i].Target.ObjRef)
}
}
Replacements []Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"`
}

View File

@ -21,6 +21,7 @@ package v1alpha1
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/kustomize/api/types"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@ -805,6 +806,101 @@ func (in *RemoteDirectOptions) DeepCopy() *RemoteDirectOptions {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplSource) DeepCopyInto(out *ReplSource) {
*out = *in
if in.ObjRef != nil {
in, out := &in.ObjRef, &out.ObjRef
*out = new(types.Target)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplSource.
func (in *ReplSource) DeepCopy() *ReplSource {
if in == nil {
return nil
}
out := new(ReplSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplTarget) DeepCopyInto(out *ReplTarget) {
*out = *in
if in.ObjRef != nil {
in, out := &in.ObjRef, &out.ObjRef
*out = new(types.Selector)
**out = **in
}
if in.FieldRefs != nil {
in, out := &in.FieldRefs, &out.FieldRefs
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplTarget.
func (in *ReplTarget) DeepCopy() *ReplTarget {
if in == nil {
return nil
}
out := new(ReplTarget)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Replacement) DeepCopyInto(out *Replacement) {
*out = *in
if in.Source != nil {
in, out := &in.Source, &out.Source
*out = new(ReplSource)
(*in).DeepCopyInto(*out)
}
if in.Target != nil {
in, out := &in.Target, &out.Target
*out = new(ReplTarget)
(*in).DeepCopyInto(*out)
}
if in.Targets != nil {
in, out := &in.Targets, &out.Targets
*out = make([]*ReplTarget, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(ReplTarget)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Replacement.
func (in *Replacement) DeepCopy() *Replacement {
if in == nil {
return nil
}
out := new(Replacement)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ReplacementTransformer) DeepCopyInto(out *ReplacementTransformer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.Replacements != nil {
in, out := &in.Replacements, &out.Replacements
*out = make([]Replacement, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplacementTransformer.
func (in *ReplacementTransformer) DeepCopy() *ReplacementTransformer {
if in == nil {

View File

@ -21,7 +21,6 @@ import (
"strings"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
@ -57,9 +56,12 @@ func New(obj map[string]interface{}) (kio.Filter, error) {
if r.Source == nil {
return nil, ErrBadConfiguration{Msg: "`from` must be specified in one replacement"}
}
if r.Target == nil {
if r.Target == nil && r.Targets == nil {
return nil, ErrBadConfiguration{Msg: "`to` must be specified in one replacement"}
}
if r.Target != nil && r.Targets != nil {
return nil, ErrBadConfiguration{Msg: "only target OR targets is allowed in one replacement, not both"}
}
if r.Source.ObjRef != nil && r.Source.Value != "" {
return nil, ErrBadConfiguration{Msg: "only one of fieldref and value is allowed in one replacement"}
}
@ -73,15 +75,22 @@ func (p *plugin) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
if err != nil {
return nil, err
}
if err := replace(items, r.Target, val); err != nil {
return nil, err
if r.Target != nil {
if err := replace(items, r.Target, val); err != nil {
return nil, err
}
}
// range handles nil case as empty list
for _, t := range r.Targets {
if err := replace(items, t, val); err != nil {
return nil, err
}
}
}
return items, nil
}
func getValue(items []*yaml.RNode, source *types.ReplSource) (*yaml.RNode, error) {
func getValue(items []*yaml.RNode, source *airshipv1.ReplSource) (*yaml.RNode, error) {
if source.Value != "" {
return yaml.NewScalarRNode(source.Value), nil
}
@ -128,7 +137,7 @@ func mutateField(rnSource *yaml.RNode) func([]*yaml.RNode) error {
}
}
func replace(items []*yaml.RNode, target *types.ReplTarget, value *yaml.RNode) error {
func replace(items []*yaml.RNode, target *airshipv1.ReplTarget, value *yaml.RNode) error {
targets, err := kyamlutils.DocumentSelector{}.
ByGVK(target.ObjRef.Group, target.ObjRef.Version, target.ObjRef.Kind).
ByName(target.ObjRef.Name).

View File

@ -1225,6 +1225,143 @@ data:
`,
expectedErr: "Error while decoding base64 encoded string: abc",
},
{
cfg: `
apiVersion: airshipit.org/v1alpha1
kind: ReplacementTransformer
metadata:
name: Test_Case_25
replacements:
- source:
value: nginx:newtag
targets:
- objref:
kind: Deployment
name: deploy1
fieldrefs:
- spec.template.spec.containers[name=nginx.latest].image
- objref:
kind: Deployment
name: deploy2
fieldrefs:
- spec.template.spec.containers[name=nginx.latest].image
- source:
value: postgres:latest
targets:
- objref:
kind: Deployment
name: deploy1
fieldrefs:
- spec.template.spec.containers.3.image
- objref:
kind: Deployment
name: deploy2
fieldrefs:
- spec.template.spec.containers.3.image
`,
in: `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx.latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
---
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:latest
name: nginx.latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:1.8.0
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`,
expectedOut: `apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy1
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:newtag
name: nginx.latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:latest
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
---
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: deploy2
spec:
template:
spec:
containers:
- image: nginx:1.7.9
name: nginx-tagged
- image: nginx:newtag
name: nginx.latest
- image: foobar:1
name: replaced-with-digest
- image: postgres:latest
name: postgresdb
initContainers:
- image: nginx
name: nginx-notag
- image: nginx@sha256:111111111111111111
name: nginx-sha256
- image: alpine:1.8.0
name: init-alpine
`,
},
}
func TestExec(t *testing.T) {