Install generated plugin code into the API module.

This commit is contained in:
Jeffrey Regan
2019-10-21 11:11:18 -07:00
parent 3f08e1546c
commit 0cf2057fc5
36 changed files with 159 additions and 143 deletions

View File

@@ -2,7 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
// Package builtinconfig provides legacy methods for
// configuring builting plugins from a common config file.
// It's better to configure them individually if using
// a custom configuration.
// configuring builtin plugins from a common config file.
// As a user, its best to configure plugins individually
// with plugin config files specified in the `transformers:`
// or `generators:` field, than to use this legacy
// configuration technique.
package builtinconfig

View File

@@ -4,8 +4,8 @@
package builtinhelpers
import (
"sigs.k8s.io/kustomize/v3/api/plugins/builtins"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/plugin/builtin"
)
//go:generate stringer -type=BuiltinPluginType
@@ -55,21 +55,21 @@ func GetBuiltinPluginType(n string) BuiltinPluginType {
}
var GeneratorFactories = map[BuiltinPluginType]func() resmap.GeneratorPlugin{
ConfigMapGenerator: builtin.NewConfigMapGeneratorPlugin,
SecretGenerator: builtin.NewSecretGeneratorPlugin,
ConfigMapGenerator: builtins.NewConfigMapGeneratorPlugin,
SecretGenerator: builtins.NewSecretGeneratorPlugin,
}
var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin{
AnnotationsTransformer: builtin.NewAnnotationsTransformerPlugin,
HashTransformer: builtin.NewHashTransformerPlugin,
ImageTagTransformer: builtin.NewImageTagTransformerPlugin,
InventoryTransformer: builtin.NewInventoryTransformerPlugin,
LabelTransformer: builtin.NewLabelTransformerPlugin,
LegacyOrderTransformer: builtin.NewLegacyOrderTransformerPlugin,
NamespaceTransformer: builtin.NewNamespaceTransformerPlugin,
PatchJson6902Transformer: builtin.NewPatchJson6902TransformerPlugin,
PatchStrategicMergeTransformer: builtin.NewPatchStrategicMergeTransformerPlugin,
PatchTransformer: builtin.NewPatchTransformerPlugin,
PrefixSuffixTransformer: builtin.NewPrefixSuffixTransformerPlugin,
ReplicaCountTransformer: builtin.NewReplicaCountTransformerPlugin,
AnnotationsTransformer: builtins.NewAnnotationsTransformerPlugin,
HashTransformer: builtins.NewHashTransformerPlugin,
ImageTagTransformer: builtins.NewImageTagTransformerPlugin,
InventoryTransformer: builtins.NewInventoryTransformerPlugin,
LabelTransformer: builtins.NewLabelTransformerPlugin,
LegacyOrderTransformer: builtins.NewLegacyOrderTransformerPlugin,
NamespaceTransformer: builtins.NewNamespaceTransformerPlugin,
PatchJson6902Transformer: builtins.NewPatchJson6902TransformerPlugin,
PatchStrategicMergeTransformer: builtins.NewPatchStrategicMergeTransformerPlugin,
PatchTransformer: builtins.NewPatchTransformerPlugin,
PrefixSuffixTransformer: builtins.NewPrefixSuffixTransformerPlugin,
ReplicaCountTransformer: builtins.NewReplicaCountTransformerPlugin,
}

View File

@@ -0,0 +1,41 @@
#!/bin/bash
#
# Generate the Go code for the generator and
# transformer factory functions in
#
# sigs.k8s.io/kustomize/v3/plugin/builtin
#
# from the raw plugin directories found _below_
# that directory.
set -e
myGoPath=$1
if [ -z ${1+x} ]; then
myGoPath=$GOPATH
fi
if [ -z "$myGoPath" ]; then
echo "Must specify a GOPATH"
exit 1
fi
dir=$myGoPath/src/sigs.k8s.io/kustomize
if [ ! -d "$dir" ]; then
echo "$dir is not a directory."
exit 1
fi
echo Generating linkable plugins...
pushd $dir >& /dev/null
GOPATH=$myGoPath go generate \
sigs.k8s.io/kustomize/v3/plugin/builtin/...
GOPATH=$myGoPath go fmt \
sigs.k8s.io/kustomize/v3/api/plugins/builtins
popd >& /dev/null
echo All done.

View File

@@ -0,0 +1,39 @@
// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/transform"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
// Add the given annotations to the given field specifications.
type AnnotationsTransformerPlugin struct {
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *AnnotationsTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.Annotations = nil
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Annotations,
)
if err != nil {
return err
}
return t.Transform(m)
}
func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin {
return &AnnotationsTransformerPlugin{}
}

View File

@@ -0,0 +1,43 @@
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"sigs.k8s.io/kustomize/v3/api/kv"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
type ConfigMapGeneratorPlugin struct {
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
types.GeneratorOptions
types.ConfigMapArgs
}
func (p *ConfigMapGeneratorPlugin) Config(
h *resmap.PluginHelpers, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.ConfigMapArgs = types.ConfigMapArgs{}
err = yaml.Unmarshal(config, p)
if p.ConfigMapArgs.Name == "" {
p.ConfigMapArgs.Name = p.Name
}
if p.ConfigMapArgs.Namespace == "" {
p.ConfigMapArgs.Namespace = p.Namespace
}
p.h = h
return
}
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
return p.h.ResmapFactory().FromConfigMapArgs(
kv.NewLoader(p.h.Loader(), p.h.Validator()),
&p.GeneratorOptions, p.ConfigMapArgs)
}
func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin {
return &ConfigMapGeneratorPlugin{}
}

View File

@@ -0,0 +1,39 @@
// Code generated by pluginator on HashTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/api/ifc"
"sigs.k8s.io/kustomize/v3/api/resmap"
)
type HashTransformerPlugin struct {
hasher ifc.KunstructuredHasher
}
func (p *HashTransformerPlugin) Config(
h *resmap.PluginHelpers, config []byte) (err error) {
p.hasher = h.ResmapFactory().RF().Hasher()
return nil
}
// Transform appends hash to generated resources.
func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
for _, res := range m.Resources() {
if res.NeedHashSuffix() {
h, err := p.hasher.Hash(res)
if err != nil {
return err
}
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
}
}
return nil
}
func NewHashTransformerPlugin() resmap.TransformerPlugin {
return &HashTransformerPlugin{}
}

View File

@@ -0,0 +1,185 @@
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"regexp"
"strings"
"sigs.k8s.io/kustomize/v3/api/transform"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/yaml"
)
// Find matching image declarations and replace
// the name, tag and/or digest.
type ImageTagTransformerPlugin struct {
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *ImageTagTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.ImageTag = types.Image{}
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
for _, path := range p.FieldSpecs {
if !r.OrgId().IsSelected(&path.Gvk) {
continue
}
err := transform.MutateField(
r.Map(), path.PathSlice(), false, p.mutateImage)
if err != nil {
return err
}
}
// Kept for backward compatibility
if err := p.findAndReplaceImage(r.Map()); err != nil && r.OrgId().Kind != `CustomResourceDefinition` {
return err
}
}
return nil
}
func (p *ImageTagTransformerPlugin) mutateImage(in interface{}) (interface{}, error) {
original, ok := in.(string)
if !ok {
return nil, fmt.Errorf("image path is not of type string but %T", in)
}
if !isImageMatched(original, p.ImageTag.Name) {
return original, nil
}
name, tag := split(original)
if p.ImageTag.NewName != "" {
name = p.ImageTag.NewName
}
if p.ImageTag.NewTag != "" {
tag = ":" + p.ImageTag.NewTag
}
if p.ImageTag.Digest != "" {
tag = "@" + p.ImageTag.Digest
}
return name + tag, nil
}
// findAndReplaceImage replaces the image name and
// tags inside one object.
// It searches the object for container session
// then loops though all images inside containers
// session, finds matched ones and update the
// image name and tag name
func (p *ImageTagTransformerPlugin) findAndReplaceImage(obj map[string]interface{}) error {
paths := []string{"containers", "initContainers"}
updated := false
for _, path := range paths {
containers, found := obj[path]
if found {
if _, err := p.updateContainers(containers); err != nil {
return err
}
updated = true
}
}
if !updated {
return p.findContainers(obj)
}
return nil
}
func (p *ImageTagTransformerPlugin) updateContainers(in interface{}) (interface{}, error) {
containers, ok := in.([]interface{})
if !ok {
return nil, fmt.Errorf(
"containers path is not of type []interface{} but %T", in)
}
for i := range containers {
container := containers[i].(map[string]interface{})
containerImage, found := container["image"]
if !found {
continue
}
imageName := containerImage.(string)
if isImageMatched(imageName, p.ImageTag.Name) {
newImage, err := p.mutateImage(imageName)
if err != nil {
return nil, err
}
container["image"] = newImage
}
}
return containers, nil
}
func (p *ImageTagTransformerPlugin) findContainers(obj map[string]interface{}) error {
for key := range obj {
switch typedV := obj[key].(type) {
case map[string]interface{}:
err := p.findAndReplaceImage(typedV)
if err != nil {
return err
}
case []interface{}:
for i := range typedV {
item := typedV[i]
typedItem, ok := item.(map[string]interface{})
if ok {
err := p.findAndReplaceImage(typedItem)
if err != nil {
return err
}
}
}
}
}
return nil
}
func isImageMatched(s, t string) bool {
// Tag values are limited to [a-zA-Z0-9_.-].
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
return pattern.MatchString(s)
}
// split separates and returns the name and tag parts
// from the image string using either colon `:` or at `@` separators.
// Note that the returned tag keeps its separator.
func split(imageName string) (name string, tag string) {
// check if image name contains a domain
// if domain is present, ignore domain and check for `:`
ic := -1
if slashIndex := strings.Index(imageName, "/"); slashIndex < 0 {
ic = strings.LastIndex(imageName, ":")
} else {
lastIc := strings.LastIndex(imageName[slashIndex:], ":")
// set ic only if `:` is present
if lastIc > 0 {
ic = slashIndex + lastIc
}
}
ia := strings.LastIndex(imageName, "@")
if ic < 0 && ia < 0 {
return imageName, ""
}
i := ic
if ia > 0 {
i = ia
}
name = imageName[:i]
tag = imageName[i:]
return
}
func NewImageTagTransformerPlugin() resmap.TransformerPlugin {
return &ImageTagTransformerPlugin{}
}

View File

@@ -0,0 +1,128 @@
// Code generated by pluginator on InventoryTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/api/hasher"
"sigs.k8s.io/kustomize/v3/api/inventory"
"sigs.k8s.io/kustomize/v3/api/kv"
"sigs.k8s.io/kustomize/v3/api/resid"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/resource"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
type InventoryTransformerPlugin struct {
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Policy string `json:"policy,omitempty" yaml:"policy,omitempty"`
}
func (p *InventoryTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.h = h
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Policy == "" {
p.Policy = types.GarbageIgnore.String()
}
if p.Policy != types.GarbageCollect.String() &&
p.Policy != types.GarbageIgnore.String() {
return fmt.Errorf(
"unrecognized garbagePolicy '%s'", p.Policy)
}
return nil
}
// Transform generates an inventory object from the input ResMap.
// This ConfigMap supports the pruning command in
// the client side tool proposed here:
// https://github.com/kubernetes/enhancements/pull/810
//
// The inventory data is written to the ConfigMap's
// annotations, rather than to the key-value pairs in
// the ConfigMap's data field, since
// 1. Keys in a ConfigMap's data field are too
// constrained for this purpose.
// 2. Using annotations allow any object to be used,
// not just a ConfigMap, should some other object
// (e.g. some App object) become more desirable
// for this purpose.
func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
inv, h, err := makeInventory(m)
if err != nil {
return err
}
args := types.ConfigMapArgs{}
args.Name = p.Name
args.Namespace = p.Namespace
opts := &types.GeneratorOptions{
Annotations: make(map[string]string),
}
opts.Annotations[inventory.HashAnnotation] = h
err = inv.UpdateAnnotations(opts.Annotations)
if err != nil {
return err
}
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
kv.NewLoader(p.h.Loader(), p.h.Validator()), opts, &args)
if err != nil {
return err
}
if p.Policy == types.GarbageCollect.String() {
for _, byeBye := range m.AllIds() {
m.Remove(byeBye)
}
}
return m.Append(cm)
}
func makeInventory(m resmap.ResMap) (
inv *inventory.Inventory, hash string, err error) {
inv = inventory.NewInventory()
var keys []string
for _, r := range m.Resources() {
ns := r.GetNamespace()
item := resid.NewResIdWithNamespace(r.GetGvk(), r.GetName(), ns)
if _, ok := inv.Current[item]; ok {
return nil, "", fmt.Errorf(
"item '%v' already in inventory", item)
}
inv.Current[item], err = computeRefs(r, m)
if err != nil {
return nil, "", err
}
keys = append(keys, item.String())
}
h, err := hasher.SortArrayAndComputeHash(keys)
return inv, h, err
}
func computeRefs(
r *resource.Resource, m resmap.ResMap) (refs []resid.ResId, err error) {
for _, refid := range r.GetRefBy() {
ref, err := m.GetByCurrentId(refid)
if err != nil {
return nil, err
}
refs = append(
refs,
resid.NewResIdWithNamespace(
ref.GetGvk(), ref.GetName(), ref.GetNamespace()))
}
return
}
func NewInventoryTransformerPlugin() resmap.TransformerPlugin {
return &InventoryTransformerPlugin{}
}

View File

@@ -0,0 +1,39 @@
// Code generated by pluginator on LabelTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/transform"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
// Add the given labels to the given field specifications.
type LabelTransformerPlugin struct {
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *LabelTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.Labels = nil
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Labels,
)
if err != nil {
return err
}
return t.Transform(m)
}
func NewLabelTransformerPlugin() resmap.TransformerPlugin {
return &LabelTransformerPlugin{}
}

View File

@@ -0,0 +1,46 @@
// Code generated by pluginator on LegacyOrderTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"sort"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/resource"
)
// Sort the resources using an ordering defined in the Gvk class.
// This puts cluster-wide basic resources with no
// dependencies (like Namespace, StorageClass, etc.)
// first, and resources with a high number of dependencies
// (like ValidatingWebhookConfiguration) last.
type LegacyOrderTransformerPlugin struct{}
// Nothing needed for configuration.
func (p *LegacyOrderTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
return nil
}
func (p *LegacyOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
resources := make([]*resource.Resource, m.Size())
ids := m.AllIds()
sort.Sort(resmap.IdSlice(ids))
for i, id := range ids {
resources[i], err = m.GetByCurrentId(id)
if err != nil {
return errors.Wrap(err, "expected match for sorting")
}
}
m.Clear()
for _, r := range resources {
m.Append(r)
}
return nil
}
func NewLegacyOrderTransformerPlugin() resmap.TransformerPlugin {
return &LegacyOrderTransformerPlugin{}
}

View File

@@ -0,0 +1,131 @@
// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/api/transform"
"sigs.k8s.io/kustomize/v3/api/resid"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/resource"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
// Change or set the namespace of non-cluster level resources.
type NamespaceTransformerPlugin struct {
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *NamespaceTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.Namespace = ""
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Namespace) == 0 {
return nil
}
for _, r := range m.Resources() {
if len(r.Map()) == 0 {
// Don't mutate empty objects?
continue
}
id := r.OrgId()
applicableFs := p.applicableFieldSpecs(id)
for _, fs := range applicableFs {
err := transform.MutateField(
r.Map(), fs.PathSlice(), fs.CreateIfNotPresent,
p.changeNamespace(r))
if err != nil {
return err
}
}
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
if len(matches) != 1 {
return fmt.Errorf("namespace tranformation produces ID conflict: %#v", matches)
}
}
return nil
}
const metaNamespace = "metadata/namespace"
// Special casing metadata.namespace since
// all objects have it, even "ClusterKind" objects
// that don't exist in a namespace (the Namespace
// object itself doesn't live in a namespace).
func (p *NamespaceTransformerPlugin) applicableFieldSpecs(id resid.ResId) []types.FieldSpec {
var res []types.FieldSpec
for _, fs := range p.FieldSpecs {
if id.IsSelected(&fs.Gvk) && (fs.Path != metaNamespace || (fs.Path == metaNamespace && id.IsNamespaceableKind())) {
res = append(res, fs)
}
}
return res
}
func (p *NamespaceTransformerPlugin) changeNamespace(
referrer *resource.Resource) func(in interface{}) (interface{}, error) {
return func(in interface{}) (interface{}, error) {
switch in.(type) {
case string:
// will happen when the metadata/namespace
// value is replaced
return p.Namespace, nil
case []interface{}:
l, _ := in.([]interface{})
for idx, item := range l {
switch item.(type) {
case map[string]interface{}:
// Will happen when mutating the subjects
// field of ClusterRoleBinding and RoleBinding
inMap, _ := item.(map[string]interface{})
if _, ok := inMap["name"]; !ok {
continue
}
name, ok := inMap["name"].(string)
if !ok {
continue
}
// The only case we need to force the namespace
// if for the "service account". "default" is
// kind of hardcoded here for right now.
if name != "default" {
continue
}
inMap["namespace"] = p.Namespace
l[idx] = inMap
default:
// nothing to do for right now
}
}
return in, nil
case map[string]interface{}:
// Will happen if the createField=true
// when the namespace is added to the
// object
inMap := in.(map[string]interface{})
if len(inMap) == 0 {
return p.Namespace, nil
} else {
return in, nil
}
default:
return in, nil
}
}
}
func NewNamespaceTransformerPlugin() resmap.TransformerPlugin {
return &NamespaceTransformerPlugin{}
}

View File

@@ -0,0 +1,100 @@
// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/api/ifc"
"sigs.k8s.io/kustomize/v3/api/resid"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
type PatchJson6902TransformerPlugin struct {
ldr ifc.Loader
decodedPatch jsonpatch.Patch
Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"`
Path string `json:"path,omitempty" yaml:"path,omitempty"`
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
}
func (p *PatchJson6902TransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.ldr = h.Loader()
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Target.Name == "" {
return fmt.Errorf("must specify the target name")
}
if p.Path == "" && p.JsonOp == "" {
return fmt.Errorf("empty file path and empty jsonOp")
}
if p.Path != "" {
if p.JsonOp != "" {
return fmt.Errorf("must specify a file path or jsonOp, not both")
}
rawOp, err := p.ldr.Load(p.Path)
if err != nil {
return err
}
p.JsonOp = string(rawOp)
if p.JsonOp == "" {
return fmt.Errorf("patch file '%s' empty seems to be empty", p.Path)
}
}
if p.JsonOp[0] != '[' {
// if it doesn't seem to be JSON, imagine
// it is YAML, and convert to JSON.
op, err := yaml.YAMLToJSON([]byte(p.JsonOp))
if err != nil {
return err
}
p.JsonOp = string(op)
}
p.decodedPatch, err = jsonpatch.DecodePatch([]byte(p.JsonOp))
if err != nil {
return errors.Wrapf(err, "decoding %s", p.JsonOp)
}
if len(p.decodedPatch) == 0 {
return fmt.Errorf(
"patch appears to be empty; file=%s, JsonOp=%s", p.Path, p.JsonOp)
}
return err
}
func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
id := resid.NewResIdWithNamespace(
resid.Gvk{
Group: p.Target.Group,
Version: p.Target.Version,
Kind: p.Target.Kind,
},
p.Target.Name,
p.Target.Namespace,
)
obj, err := m.GetById(id)
if err != nil {
return err
}
rawObj, err := obj.MarshalJSON()
if err != nil {
return err
}
modifiedObj, err := p.decodedPatch.Apply(rawObj)
if err != nil {
return errors.Wrapf(
err, "failed to apply json patch '%s'", p.JsonOp)
}
return obj.UnmarshalJSON(modifiedObj)
}
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {
return &PatchJson6902TransformerPlugin{}
}

View File

@@ -0,0 +1,90 @@
// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/resource"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
type PatchStrategicMergeTransformerPlugin struct {
h *resmap.PluginHelpers
loadedPatches []*resource.Resource
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
}
func (p *PatchStrategicMergeTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.h = h
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if len(p.Paths) == 0 && p.Patches == "" {
return fmt.Errorf("empty file path and empty patch content")
}
if len(p.Paths) != 0 {
for _, onePath := range p.Paths {
res, err := p.h.ResmapFactory().RF().SliceFromBytes([]byte(onePath))
if err == nil {
p.loadedPatches = append(p.loadedPatches, res...)
continue
}
res, err = p.h.ResmapFactory().RF().SliceFromPatches(
p.h.Loader(), []types.PatchStrategicMerge{onePath})
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
}
}
if p.Patches != "" {
res, err := p.h.ResmapFactory().RF().SliceFromBytes([]byte(p.Patches))
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
}
if len(p.loadedPatches) == 0 {
return fmt.Errorf(
"patch appears to be empty; files=%v, Patch=%s", p.Paths, p.Patches)
}
return err
}
func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error {
patches, err := p.h.ResmapFactory().MergePatches(p.loadedPatches)
if err != nil {
return err
}
for _, patch := range patches.Resources() {
target, err := m.GetById(patch.OrgId())
if err != nil {
return err
}
err = target.Patch(patch.Kunstructured)
if err != nil {
return err
}
// remove the resource from resmap
// when the patch is to $patch: delete that target
if len(target.Map()) == 0 {
err = m.Remove(target.CurId())
if err != nil {
return err
}
}
}
return nil
}
func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin {
return &PatchStrategicMergeTransformerPlugin{}
}

View File

@@ -0,0 +1,145 @@
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/resource"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
type PatchTransformerPlugin struct {
loadedPatch *resource.Resource
decodedPatch jsonpatch.Patch
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
}
func (p *PatchTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Patch == "" && p.Path == "" {
err = fmt.Errorf(
"must specify one of patch and path in\n%s", string(c))
return
}
if p.Patch != "" && p.Path != "" {
err = fmt.Errorf(
"patch and path can't be set at the same time\n%s", string(c))
return
}
var in []byte
if p.Path != "" {
in, err = h.Loader().Load(p.Path)
if err != nil {
return
}
}
if p.Patch != "" {
in = []byte(p.Patch)
}
patchSM, errSM := h.ResmapFactory().RF().FromBytes(in)
patchJson, errJson := jsonPatchFromBytes(in)
if errSM != nil && errJson != nil {
err = fmt.Errorf(
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
return
}
if errSM == nil && errJson != nil {
p.loadedPatch = patchSM
}
if errJson == nil && errSM != nil {
p.decodedPatch = patchJson
}
if patchSM != nil && patchJson != nil {
err = fmt.Errorf(
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
}
return nil
}
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
if p.loadedPatch != nil && p.Target == nil {
target, err := m.GetById(p.loadedPatch.OrgId())
if err != nil {
return err
}
err = target.Patch(p.loadedPatch.Kunstructured)
if err != nil {
return err
}
return nil
}
if p.Target == nil {
return fmt.Errorf("must specify a target for patch %s", p.Patch)
}
resources, err := m.Select(*p.Target)
if err != nil {
return err
}
for _, res := range resources {
if p.decodedPatch != nil {
rawObj, err := res.MarshalJSON()
if err != nil {
return err
}
modifiedObj, err := p.decodedPatch.Apply(rawObj)
if err != nil {
return errors.Wrapf(
err, "failed to apply json patch '%s'", p.Patch)
}
err = res.UnmarshalJSON(modifiedObj)
if err != nil {
return err
}
}
if p.loadedPatch != nil {
patchCopy := p.loadedPatch.DeepCopy()
patchCopy.SetName(res.GetName())
patchCopy.SetNamespace(res.GetNamespace())
patchCopy.SetGvk(res.GetGvk())
err = res.Patch(patchCopy.Kunstructured)
if err != nil {
return err
}
}
}
return nil
}
// jsonPatchFromBytes loads a Json 6902 patch from
// a bytes input
func jsonPatchFromBytes(
in []byte) (jsonpatch.Patch, error) {
ops := string(in)
if ops == "" {
return nil, fmt.Errorf("empty json patch operations")
}
if ops[0] != '[' {
jsonOps, err := yaml.YAMLToJSON(in)
if err != nil {
return nil, err
}
ops = string(jsonOps)
}
return jsonpatch.DecodePatch([]byte(ops))
}
func NewPatchTransformerPlugin() resmap.TransformerPlugin {
return &PatchTransformerPlugin{}
}

View File

@@ -0,0 +1,123 @@
// Code generated by pluginator on PrefixSuffixTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"errors"
"fmt"
"sigs.k8s.io/kustomize/v3/api/transform"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/kustomize/v3/api/resid"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/yaml"
)
// Add the given prefix and suffix to the field.
type PrefixSuffixTransformerPlugin struct {
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
// Not placed in a file yet due to lack of demand.
var prefixSuffixFieldSpecsToSkip = []types.FieldSpec{
{
Gvk: resid.Gvk{Kind: "CustomResourceDefinition"},
},
{
Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"},
},
}
func (p *PrefixSuffixTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.Prefix = ""
p.Suffix = ""
p.FieldSpecs = nil
err = yaml.Unmarshal(c, p)
if err != nil {
return
}
if p.FieldSpecs == nil {
return errors.New("fieldSpecs is not expected to be nil")
}
return
}
func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
// Even if both the Prefix and Suffix are empty we want
// to proceed with the transformation. This allows to add contextual
// information to the resources (AddNamePrefix and AddNameSuffix).
for _, r := range m.Resources() {
if p.shouldSkip(r.OrgId()) {
// Don't change the actual definition
// of a CRD.
continue
}
id := r.OrgId()
// current default configuration contains
// only one entry: "metadata/name" with no GVK
for _, path := range p.FieldSpecs {
if !id.IsSelected(&path.Gvk) {
// With the currrent default configuration,
// because no Gvk is specified, so a wild
// card
continue
}
if smellsLikeANameChange(&path) {
// "metadata/name" is the only field.
// this will add a prefix and a suffix
// to the resource even if those are
// empty
r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix)
}
// the addPrefixSuffix method will not
// change the name if both the prefix and suffix
// are empty.
err := transform.MutateField(
r.Map(),
path.PathSlice(),
path.CreateIfNotPresent,
p.addPrefixSuffix)
if err != nil {
return err
}
}
}
return nil
}
func smellsLikeANameChange(fs *types.FieldSpec) bool {
return fs.Path == "metadata/name"
}
func (p *PrefixSuffixTransformerPlugin) shouldSkip(
id resid.ResId) bool {
for _, path := range prefixSuffixFieldSpecsToSkip {
if id.IsSelected(&path.Gvk) {
return true
}
}
return false
}
func (p *PrefixSuffixTransformerPlugin) addPrefixSuffix(
in interface{}) (interface{}, error) {
s, ok := in.(string)
if !ok {
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
}
return fmt.Sprintf("%s%s%s", p.Prefix, s, p.Suffix), nil
}
func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
return &PrefixSuffixTransformerPlugin{}
}

View File

@@ -0,0 +1,89 @@
// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/api/transform"
"sigs.k8s.io/kustomize/v3/api/resid"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
// Find matching replicas declarations and replace the count.
// Eases the kustomization configuration of replica changes.
type ReplicaCountTransformerPlugin struct {
Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *ReplicaCountTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.Replica = types.Replica{}
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
found := false
for i, replicaSpec := range p.FieldSpecs {
matcher := p.createMatcher(i)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher)
matchCurrent := m.GetMatchingResourcesByCurrentId(matcher)
for _, res := range append(matchOriginal, matchCurrent...) {
found = true
err := transform.MutateField(
res.Map(), replicaSpec.PathSlice(),
replicaSpec.CreateIfNotPresent, p.addReplicas)
if err != nil {
return err
}
}
}
if !found {
gvks := make([]string, len(p.FieldSpecs))
for i, replicaSpec := range p.FieldSpecs {
gvks[i] = replicaSpec.Gvk.String()
}
return fmt.Errorf("resource with name %s does not match a config with the following GVK %v",
p.Replica.Name, gvks)
}
return nil
}
// Match Replica.Name and FieldSpec
func (p *ReplicaCountTransformerPlugin) createMatcher(i int) resmap.IdMatcher {
return func(r resid.ResId) bool {
return r.Name == p.Replica.Name &&
r.Gvk.IsSelected(&p.FieldSpecs[i].Gvk)
}
}
func (p *ReplicaCountTransformerPlugin) addReplicas(in interface{}) (interface{}, error) {
switch m := in.(type) {
case int64:
// Was already in the field.
case map[string]interface{}:
if len(m) != 0 {
// A map was already in the replicas field, don't want to
// discard this data silently.
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
}
// Just got added, default type is map, but we can return anything.
default:
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
}
return p.Replica.Count, nil
}
func NewReplicaCountTransformerPlugin() resmap.TransformerPlugin {
return &ReplicaCountTransformerPlugin{}
}

View File

@@ -0,0 +1,42 @@
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
// pluginator {Version:unknown GitCommit:$Format:%H$ BuildDate:1970-01-01T00:00:00Z GoOs:linux GoArch:amd64}
package builtins
import (
"sigs.k8s.io/kustomize/v3/api/kv"
"sigs.k8s.io/kustomize/v3/api/resmap"
"sigs.k8s.io/kustomize/v3/api/types"
"sigs.k8s.io/yaml"
)
type SecretGeneratorPlugin struct {
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
types.GeneratorOptions
types.SecretArgs
}
func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.SecretArgs = types.SecretArgs{}
err = yaml.Unmarshal(config, p)
if p.SecretArgs.Name == "" {
p.SecretArgs.Name = p.Name
}
if p.SecretArgs.Namespace == "" {
p.SecretArgs.Namespace = p.Namespace
}
p.h = h
return
}
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
return p.h.ResmapFactory().FromSecretArgs(
kv.NewLoader(p.h.Loader(), p.h.Validator()),
&p.GeneratorOptions, p.SecretArgs)
}
func NewSecretGeneratorPlugin() resmap.GeneratorPlugin {
return &SecretGeneratorPlugin{}
}

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package builtins holds code generated from the builting plugins.
// Package builtins holds code generated from the builtin plugins.
// The "builtin" plugins are written as normal plugins and can
// be used as such, but they are also used to generate the code
// in this package so they can be statically linked to client code.

View File

@@ -0,0 +1,17 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// +build tools
// This file exists to declare that its containing
// package explicitly depends on the pluginator
// tool (via go:generate directives)
package builtins
// TODO: replace this, with the appropriate version
// once the API is launched, and the new pluginator
// has been compiled against it and released.
//
// import (
// _ "sigs.k8s.io/kustomize/pluginator"
// )

118
api/plugins/doc.go Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
/*
Read docs/plugins.md first for an overview of kustomize plugins.
BUILTIN PLUGIN CONFIGURATION
There are two kinds of plugins, Go plugins (shared
object library) and exec plugins (independent binary).
For performance and standardized testing reasons, all
builtin plugins are Go plugins (not exec plugins).
Using "SecretGenerator" as an example in what
follows.
The plugin config file looks like
apiVersion: builtin
kind: SecretGenerator
metadata:
name: whatever
otherField1: whatever
otherField2: whatever
...
The apiVersion must be 'builtin'.
The kind is the CamelCase name of the plugin.
The source for a builtin plugin must be at:
repo=$GOPATH/src/sigs.k8s.io/kustomize
${repo}/v3/plugin/builtin/LOWERCASE(${kind})/${kind}
k8s wants 'kind' values to follow CamelCase, while
Go style doesn't like but does allow such names.
The lowercased value of kind is used as the name of the
directory holding the plugin, its test, and any
optional associated files (possibly a go.mod file).
BUILTIN PLUGIN GENERATION
The `pluginator` program is a code generator that
converts kustomize generator (G) and/or
transformer (T) Go plugins to statically linkable
code.
It arises from following requirements:
* extension
kustomize does two things - generate or
transform k8s resources. Plugins let
users write their own G&T's without
having to fork kustomize and learn its
internals.
* dogfooding
A G&T extension framework one can trust
should be used by its authors to deliver
builtin G&T's.
* distribution
kustomize should be distributable via
`go get` and should run where Go
programs are expected to run.
The extension requirement led to building
a framework that accommodates writing a
G or T as either
* an 'exec' plugin (any executable file
runnable as a kustomize subprocess), or
* as a Go plugin - see
https://golang.org/pkg/plugin.
The dogfooding (and an implicit performance
requirement) requires a 'builtin' G or T to
be written as a Go plugin.
The distribution ('go get') requirement demands
conversion of Go plugins to statically linked
code, hence this program.
TO GENERATE CODE
repo=$GOPATH/src/sigs.k8s.io/kustomize
cd $repo/v3/plugin/builtin
go generate ./...
See travis/pre-commit.sh for canonical way
to execute the above.
This creates
$repo/api/plugins/builtins/SecretGenerator.go
etc.
Generated plugins are used in kustomize via
package whatever
import sigs.k8s.io/kustomize/v3/plugins/builtins
...
g := builtin.NewSecretGenerator()
g.Config(h, k)
resources, err := g.Generate()
err = g.Transform(resources)
// Eventually emit resources.
*/
package plugins