Skip to content

Commit

Permalink
add replacement filter to support replacmenttransformer
Browse files Browse the repository at this point in the history
  • Loading branch information
natasha41575 committed Mar 17, 2021
1 parent 710db98 commit e7cc1a1
Show file tree
Hide file tree
Showing 4 changed files with 436 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/filters/replacement/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package replacement contains a kio.Filter implementation of the kustomize
// replacement transformer (accepts sources and looks for targets to replace
// their values with values form the sources).
package replacement
56 changes: 56 additions & 0 deletions api/filters/replacement/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package replacement

import (
"bytes"
"log"
"os"

"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

func ExampleFilter() {
f := Filter{
Replacements: []types.Replacement{},
}
err := yaml.Unmarshal([]byte(`
replacements:
- source:
value: 99
target:
objref:
kind: Foo
fieldrefs:
- spec.replicas`), &f)
if err != nil {
log.Fatal(err)
}

err = kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
spec:
replicas: 3
`)}},
Filters: []kio.Filter{f},
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
}.Execute()
if err != nil {
log.Fatal(err)
}

// Output:
// apiVersion: example.com/v1
// kind: Foo
// metadata:
// name: instance
// spec:
// replicas: 99
}
111 changes: 111 additions & 0 deletions api/filters/replacement/replacement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package replacement

import (
"fmt"
"strings"

"sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

type Filter struct {
Replacements []types.Replacement
}

var _ kio.Filter = Filter{}

// Filter replaces values of targets with values from sources
// TODO (#3492): Connect this to a replacement transformer plugin
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var result []*yaml.RNode
// TODO (#3492): Move getReplacement to the replacement transformer plugin
// and avoid creating a new resmap here
rf := provider.NewDefaultDepProvider().GetResourceFactory()
m, err := resmap.NewFactory(rf).NewResMapFromRNodeSlice(nodes)
if err != nil {
return nil, err
}
for i := range nodes {
r, err := applyReplacements(f.Replacements, nodes[i], m)
if err != nil {
return nil, err
}
if r != nil {
result = append(result, r)
}
}
return result, nil
}

func applyReplacements(replacements []types.Replacement, node *yaml.RNode,
m resmap.ResMap) (*yaml.RNode, error) {
for _, r := range replacements {
value, err := getReplacement(m, r.Source)
if err != nil {
return node, err
}
node, err := applyReplacement(r, node, value)
if err != nil {
return node, err
}
}
return node, nil
}

func applyReplacement(r types.Replacement, node *yaml.RNode, value *yaml.RNode) (*yaml.RNode, error) {
for i := range r.Target.FieldRefs {
fieldref := strings.Split(r.Target.FieldRefs[i], ".")
// TODO (#3492): Support using map keys in the fieldref (e.g. .spec.containers[name=nginx])
target, err := node.Pipe(yaml.Lookup(fieldref...))
if err != nil {
return node, err
}
if target == nil {
continue
}
target.SetYNode(value.YNode())
}
return node, nil
}

// TODO (#3492): Move this to the replacement transformer plugin, and have it pass the
// source value to the filter. The plugin will already have a ResMap so it will not
// need to convert the nodes to a ResMap.
func getReplacement(m resmap.ResMap, source *types.ReplSource) (*yaml.RNode, error) {
if source.Value != "" {
return yaml.Parse(source.Value)
}
objRef := source.ObjRef
fieldRef := source.FieldRef
s := types.Selector{
Gvk: objRef.Gvk,
Name: objRef.Name,
Namespace: objRef.Namespace,
}
resources, err := m.Select(s)
if err != nil {
return nil, err
}
if len(resources) > 1 {
return nil, fmt.Errorf("found more than one resources matching from %v", resources)
}
if len(resources) == 0 {
return nil, fmt.Errorf("failed to find one resource matching from %v", objRef)
}
if fieldRef == "" {
fieldRef = ".metadata.name"
}
fieldRefSlice := strings.Split(fieldRef, ".")
// TODO (#3492): Support using map keys in the fieldref (e.g. .spec.containers[name=nginx])
rn, err := resources[0].AsRNode().Pipe(yaml.Lookup(fieldRefSlice...))
if err != nil {
return nil, err
}
return rn, nil
}
Loading

0 comments on commit e7cc1a1

Please sign in to comment.