mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 02:20:53 +00:00
Make api/plugins
This commit is contained in:
37
api/plugins/builtins/builtinplugintype_string.go
Normal file
37
api/plugins/builtins/builtinplugintype_string.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Code generated by "stringer -type=BuiltinPluginType"; DO NOT EDIT.
|
||||
|
||||
package builtins
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[Unknown-0]
|
||||
_ = x[SecretGenerator-1]
|
||||
_ = x[ConfigMapGenerator-2]
|
||||
_ = x[ReplicaCountTransformer-3]
|
||||
_ = x[NamespaceTransformer-4]
|
||||
_ = x[PatchJson6902Transformer-5]
|
||||
_ = x[PatchStrategicMergeTransformer-6]
|
||||
_ = x[PatchTransformer-7]
|
||||
_ = x[LabelTransformer-8]
|
||||
_ = x[AnnotationsTransformer-9]
|
||||
_ = x[PrefixSuffixTransformer-10]
|
||||
_ = x[ImageTagTransformer-11]
|
||||
_ = x[HashTransformer-12]
|
||||
_ = x[InventoryTransformer-13]
|
||||
_ = x[LegacyOrderTransformer-14]
|
||||
}
|
||||
|
||||
const _BuiltinPluginType_name = "UnknownSecretGeneratorConfigMapGeneratorReplicaCountTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerLabelTransformerAnnotationsTransformerPrefixSuffixTransformerImageTagTransformerHashTransformerInventoryTransformerLegacyOrderTransformer"
|
||||
|
||||
var _BuiltinPluginType_index = [...]uint16{0, 7, 22, 40, 63, 83, 107, 137, 153, 169, 191, 214, 233, 248, 268, 290}
|
||||
|
||||
func (i BuiltinPluginType) String() string {
|
||||
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {
|
||||
return "BuiltinPluginType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _BuiltinPluginType_name[_BuiltinPluginType_index[i]:_BuiltinPluginType_index[i+1]]
|
||||
}
|
||||
75
api/plugins/builtins/builtins.go
Normal file
75
api/plugins/builtins/builtins.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/v3/api/resmap"
|
||||
"sigs.k8s.io/kustomize/v3/plugin/builtin"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=BuiltinPluginType
|
||||
type BuiltinPluginType int
|
||||
|
||||
const (
|
||||
Unknown BuiltinPluginType = iota
|
||||
SecretGenerator
|
||||
ConfigMapGenerator
|
||||
ReplicaCountTransformer
|
||||
NamespaceTransformer
|
||||
PatchJson6902Transformer
|
||||
PatchStrategicMergeTransformer
|
||||
PatchTransformer
|
||||
LabelTransformer
|
||||
AnnotationsTransformer
|
||||
PrefixSuffixTransformer
|
||||
ImageTagTransformer
|
||||
HashTransformer
|
||||
InventoryTransformer
|
||||
LegacyOrderTransformer
|
||||
)
|
||||
|
||||
var stringToBuiltinPluginTypeMap map[string]BuiltinPluginType
|
||||
|
||||
func init() {
|
||||
stringToBuiltinPluginTypeMap = makeStringToBuiltinPluginTypeMap()
|
||||
}
|
||||
|
||||
func makeStringToBuiltinPluginTypeMap() (result map[string]BuiltinPluginType) {
|
||||
result = make(map[string]BuiltinPluginType, 23)
|
||||
for k := range GeneratorFactories {
|
||||
result[k.String()] = k
|
||||
}
|
||||
for k := range TransformerFactories {
|
||||
result[k.String()] = k
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetBuiltinPluginType(n string) BuiltinPluginType {
|
||||
result, ok := stringToBuiltinPluginTypeMap[n]
|
||||
if ok {
|
||||
return result
|
||||
}
|
||||
return Unknown
|
||||
}
|
||||
|
||||
var GeneratorFactories = map[BuiltinPluginType]func() resmap.GeneratorPlugin{
|
||||
SecretGenerator: builtin.NewSecretGeneratorPlugin,
|
||||
ConfigMapGenerator: builtin.NewConfigMapGeneratorPlugin,
|
||||
}
|
||||
|
||||
var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin{
|
||||
NamespaceTransformer: builtin.NewNamespaceTransformerPlugin,
|
||||
ReplicaCountTransformer: builtin.NewReplicaCountTransformerPlugin,
|
||||
PatchJson6902Transformer: builtin.NewPatchJson6902TransformerPlugin,
|
||||
PatchStrategicMergeTransformer: builtin.NewPatchStrategicMergeTransformerPlugin,
|
||||
PatchTransformer: builtin.NewPatchTransformerPlugin,
|
||||
LabelTransformer: builtin.NewLabelTransformerPlugin,
|
||||
AnnotationsTransformer: builtin.NewAnnotationsTransformerPlugin,
|
||||
PrefixSuffixTransformer: builtin.NewPrefixSuffixTransformerPlugin,
|
||||
ImageTagTransformer: builtin.NewImageTagTransformerPlugin,
|
||||
HashTransformer: builtin.NewHashTransformerPlugin,
|
||||
InventoryTransformer: builtin.NewInventoryTransformerPlugin,
|
||||
LegacyOrderTransformer: builtin.NewLegacyOrderTransformerPlugin,
|
||||
}
|
||||
146
api/plugins/compiler/compiler.go
Normal file
146
api/plugins/compiler/compiler.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/pgmconfig"
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/config"
|
||||
)
|
||||
|
||||
// Compiler creates Go plugin object files.
|
||||
//
|
||||
// Source code is read from
|
||||
// ${srcRoot}/${g}/${v}/${k}.go
|
||||
//
|
||||
// Object code is written to
|
||||
// ${objRoot}/${g}/${v}/${k}.so
|
||||
type Compiler struct {
|
||||
srcRoot string
|
||||
objRoot string
|
||||
}
|
||||
|
||||
// DefaultSrcRoot guesses where the user
|
||||
// has her ${g}/${v}/$lower(${k})/${k}.go files.
|
||||
func DefaultSrcRoot() (string, error) {
|
||||
var nope []string
|
||||
var root string
|
||||
|
||||
root = filepath.Join(
|
||||
os.Getenv("GOPATH"), "src",
|
||||
config.DomainName, pgmconfig.ProgramName, config.PluginRoot)
|
||||
if FileExists(root) {
|
||||
return root, nil
|
||||
}
|
||||
nope = append(nope, root)
|
||||
|
||||
root = config.DefaultPluginConfig().DirectoryPath
|
||||
if FileExists(root) {
|
||||
return root, nil
|
||||
}
|
||||
nope = append(nope, root)
|
||||
|
||||
root = filepath.Join(
|
||||
config.HomeDir(), pgmconfig.ProgramName, config.PluginRoot)
|
||||
if FileExists(root) {
|
||||
return root, nil
|
||||
}
|
||||
nope = append(nope, root)
|
||||
|
||||
return "", fmt.Errorf(
|
||||
"no default src root; tried %v", nope)
|
||||
}
|
||||
|
||||
// NewCompiler returns a new compiler instance.
|
||||
func NewCompiler(srcRoot, objRoot string) *Compiler {
|
||||
return &Compiler{srcRoot: srcRoot, objRoot: objRoot}
|
||||
}
|
||||
|
||||
// ObjRoot is root of compilation target tree.
|
||||
func (b *Compiler) ObjRoot() string {
|
||||
return b.objRoot
|
||||
}
|
||||
|
||||
// SrcRoot is where to find src.
|
||||
func (b *Compiler) SrcRoot() string {
|
||||
return b.srcRoot
|
||||
}
|
||||
|
||||
func goBin() string {
|
||||
return filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||
}
|
||||
|
||||
// Compile reads ${srcRoot}/${g}/${v}/${k}.go
|
||||
// and writes ${objRoot}/${g}/${v}/${k}.so
|
||||
func (b *Compiler) Compile(g, v, k string) error {
|
||||
lowK := strings.ToLower(k)
|
||||
objDir := filepath.Join(b.objRoot, g, v, lowK)
|
||||
objFile := filepath.Join(objDir, k) + ".so"
|
||||
if RecentFileExists(objFile) {
|
||||
// Skip rebuilding it.
|
||||
return nil
|
||||
}
|
||||
err := os.MkdirAll(objDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcFile := filepath.Join(b.srcRoot, g, v, lowK, k) + ".go"
|
||||
if !FileExists(srcFile) {
|
||||
// Handy for tests of lone plugins.
|
||||
s := k + ".go"
|
||||
if !FileExists(s) {
|
||||
return fmt.Errorf(
|
||||
"cannot find source at '%s' or '%s'", srcFile, s)
|
||||
|
||||
}
|
||||
srcFile = s
|
||||
}
|
||||
commands := []string{
|
||||
"build",
|
||||
"-buildmode",
|
||||
"plugin",
|
||||
"-o", objFile, srcFile,
|
||||
}
|
||||
goBin := goBin()
|
||||
if !FileExists(goBin) {
|
||||
return fmt.Errorf(
|
||||
"cannot find go compiler %s", goBin)
|
||||
}
|
||||
cmd := exec.Command(goBin, commands...)
|
||||
cmd.Env = os.Environ()
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf(
|
||||
"compiler error building %s: %v", srcFile, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// True if file less than 3 minutes old, i.e. not
|
||||
// accidentally left over from some earlier build.
|
||||
func RecentFileExists(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
age := time.Now().Sub(fi.ModTime())
|
||||
return age.Minutes() < 3
|
||||
}
|
||||
|
||||
func FileExists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
66
api/plugins/compiler/compiler_test.go
Normal file
66
api/plugins/compiler/compiler_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package compiler_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/v3/api/plugins/compiler"
|
||||
)
|
||||
|
||||
// Regression coverage over compiler behavior.
|
||||
func TestCompiler(t *testing.T) {
|
||||
configRoot, err := ioutil.TempDir("", "kustomize-compiler-test")
|
||||
if err != nil {
|
||||
t.Errorf("failed to make temp dir: %v", err)
|
||||
}
|
||||
srcRoot, err := DefaultSrcRoot()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c := NewCompiler(srcRoot, configRoot)
|
||||
if configRoot != c.ObjRoot() {
|
||||
t.Errorf("unexpected objRoot %s", c.ObjRoot())
|
||||
}
|
||||
|
||||
expectObj := filepath.Join(
|
||||
c.ObjRoot(),
|
||||
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||
}
|
||||
err = c.Compile("someteam.example.com", "v1", "DatePrefixer")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !RecentFileExists(expectObj) {
|
||||
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||
}
|
||||
|
||||
expectObj = filepath.Join(
|
||||
c.ObjRoot(),
|
||||
"builtin", "", "secretgenerator", "SecretGenerator.so")
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||
}
|
||||
err = c.Compile("builtin", "", "SecretGenerator")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !RecentFileExists(expectObj) {
|
||||
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(c.ObjRoot())
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"removing temp dir: %s %v", c.ObjRoot(), err)
|
||||
}
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("cleanup failed; still see: %s", expectObj)
|
||||
}
|
||||
}
|
||||
95
api/plugins/config/config.go
Normal file
95
api/plugins/config/config.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"sigs.k8s.io/kustomize/v3/api/pgmconfig"
|
||||
"sigs.k8s.io/kustomize/v3/api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// Used with Go plugins.
|
||||
PluginSymbol = "KustomizePlugin"
|
||||
|
||||
// Location of builtins.
|
||||
BuiltinPluginPackage = "builtin"
|
||||
|
||||
// ApiVersion of builtins.
|
||||
BuiltinPluginApiVersion = BuiltinPluginPackage
|
||||
|
||||
// Domain from which kustomize code is imported, for locating
|
||||
// plugin source code under $GOPATH.
|
||||
DomainName = "sigs.k8s.io"
|
||||
|
||||
// Name of directory housing all plugins.
|
||||
PluginRoot = "plugin"
|
||||
|
||||
flagEnablePluginsName = "enable_alpha_plugins"
|
||||
flagEnablePluginsHelp = `enable plugins, an alpha feature.
|
||||
See https://github.com/kubernetes-sigs/kustomize/blob/master/docs/plugins/README.md
|
||||
`
|
||||
flagErrorFmt = `
|
||||
unable to load external plugin %s because plugins disabled
|
||||
specify the flag
|
||||
--%s
|
||||
to %s`
|
||||
)
|
||||
|
||||
func ActivePluginConfig() *types.PluginConfig {
|
||||
pc := DefaultPluginConfig()
|
||||
pc.Enabled = true
|
||||
return pc
|
||||
}
|
||||
|
||||
func DefaultPluginConfig() *types.PluginConfig {
|
||||
return &types.PluginConfig{
|
||||
Enabled: false,
|
||||
DirectoryPath: filepath.Join(configRoot(), PluginRoot),
|
||||
}
|
||||
}
|
||||
|
||||
func NotEnabledErr(name string) error {
|
||||
return fmt.Errorf(
|
||||
flagErrorFmt,
|
||||
name,
|
||||
flagEnablePluginsName,
|
||||
flagEnablePluginsHelp)
|
||||
}
|
||||
|
||||
func AddFlagEnablePlugins(set *pflag.FlagSet, v *bool) {
|
||||
set.BoolVar(
|
||||
v, flagEnablePluginsName,
|
||||
false, flagEnablePluginsHelp)
|
||||
}
|
||||
|
||||
// Use https://github.com/kirsle/configdir instead?
|
||||
func configRoot() string {
|
||||
dir := os.Getenv(pgmconfig.XdgConfigHome)
|
||||
if len(dir) == 0 {
|
||||
dir = filepath.Join(
|
||||
HomeDir(), pgmconfig.DefaultConfigSubdir)
|
||||
}
|
||||
return filepath.Join(dir, pgmconfig.ProgramName)
|
||||
}
|
||||
|
||||
func HomeDir() string {
|
||||
home := os.Getenv(homeEnv())
|
||||
if len(home) > 0 {
|
||||
return home
|
||||
}
|
||||
return "~"
|
||||
}
|
||||
|
||||
func homeEnv() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return "USERPROFILE"
|
||||
}
|
||||
return "HOME"
|
||||
}
|
||||
45
api/plugins/config/config_test.go
Normal file
45
api/plugins/config/config_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/pgmconfig"
|
||||
)
|
||||
|
||||
func TestConfigDirNoXdg(t *testing.T) {
|
||||
xdg, isSet := os.LookupEnv(pgmconfig.XdgConfigHome)
|
||||
if isSet {
|
||||
os.Unsetenv(pgmconfig.XdgConfigHome)
|
||||
}
|
||||
s := configRoot()
|
||||
if isSet {
|
||||
os.Setenv(pgmconfig.XdgConfigHome, xdg)
|
||||
}
|
||||
if !strings.HasSuffix(
|
||||
s,
|
||||
rootedPath(pgmconfig.DefaultConfigSubdir, pgmconfig.ProgramName)) {
|
||||
t.Fatalf("unexpected config dir: %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func rootedPath(elem ...string) string {
|
||||
return string(filepath.Separator) + filepath.Join(elem...)
|
||||
}
|
||||
|
||||
func TestConfigDirWithXdg(t *testing.T) {
|
||||
xdg, isSet := os.LookupEnv(pgmconfig.XdgConfigHome)
|
||||
os.Setenv(pgmconfig.XdgConfigHome, rootedPath("blah"))
|
||||
s := configRoot()
|
||||
if isSet {
|
||||
os.Setenv(pgmconfig.XdgConfigHome, xdg)
|
||||
}
|
||||
if s != rootedPath("blah", pgmconfig.ProgramName) {
|
||||
t.Fatalf("unexpected config dir: %s", s)
|
||||
}
|
||||
}
|
||||
268
api/plugins/execplugin/execplugin.go
Normal file
268
api/plugins/execplugin/execplugin.go
Normal file
@@ -0,0 +1,268 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package execplugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
idAnnotation = "kustomize.config.k8s.io/id"
|
||||
HashAnnotation = "kustomize.config.k8s.io/needs-hash"
|
||||
BehaviorAnnotation = "kustomize.config.k8s.io/behavior"
|
||||
tmpConfigFilePrefix = "kust-plugin-config-"
|
||||
)
|
||||
|
||||
// ExecPlugin record the name and args of an executable
|
||||
// It triggers the executable generator and transformer
|
||||
type ExecPlugin struct {
|
||||
// absolute path of the executable
|
||||
path string
|
||||
|
||||
// Optional command line arguments to the executable
|
||||
// pulled from specially named fields in cfg.
|
||||
// This is for executables that don't want to parse YAML.
|
||||
args []string
|
||||
|
||||
// Plugin configuration data.
|
||||
cfg []byte
|
||||
|
||||
// PluginHelpers
|
||||
h *resmap.PluginHelpers
|
||||
}
|
||||
|
||||
func NewExecPlugin(p string) *ExecPlugin {
|
||||
return &ExecPlugin{path: p}
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) Path() string {
|
||||
return p.path
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) Args() []string {
|
||||
return p.args
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) Cfg() []byte {
|
||||
return p.cfg
|
||||
}
|
||||
|
||||
// isAvailable checks to see if the plugin is available
|
||||
func (p *ExecPlugin) IsAvailable() bool {
|
||||
f, err := os.Stat(p.path)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return f.Mode()&0111 != 0000
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) Config(h *resmap.PluginHelpers, config []byte) error {
|
||||
p.h = h
|
||||
p.cfg = config
|
||||
return p.processOptionalArgsFields()
|
||||
}
|
||||
|
||||
type argsConfig struct {
|
||||
ArgsOneLiner string `json:"argsOneLiner,omitempty" yaml:"argsOneLiner,omitempty"`
|
||||
ArgsFromFile string `json:"argsFromFile,omitempty" yaml:"argsFromFile,omitempty"`
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) processOptionalArgsFields() error {
|
||||
var c argsConfig
|
||||
yaml.Unmarshal(p.cfg, &c)
|
||||
if c.ArgsOneLiner != "" {
|
||||
p.args = strings.Split(c.ArgsOneLiner, " ")
|
||||
}
|
||||
if c.ArgsFromFile != "" {
|
||||
content, err := p.h.Loader().Load(c.ArgsFromFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, x := range strings.Split(string(content), "\n") {
|
||||
x := strings.TrimLeft(x, " ")
|
||||
if x != "" {
|
||||
p.args = append(p.args, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) Generate() (resmap.ResMap, error) {
|
||||
output, err := p.invokePlugin(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm, err := p.h.ResmapFactory().NewResMapFromBytes(output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.UpdateResourceOptions(rm)
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) Transform(rm resmap.ResMap) error {
|
||||
// add ResIds as annotations to all objects so that we can add them back
|
||||
inputRM, err := p.getResMapWithIdAnnotation(rm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// encode the ResMap so it can be fed to the plugin
|
||||
resources, err := inputRM.AsYaml()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// invoke the plugin with resources as the input
|
||||
output, err := p.invokePlugin(resources)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v %s", err, string(output))
|
||||
}
|
||||
|
||||
// update the original ResMap based on the output
|
||||
return p.updateResMapValues(output, rm)
|
||||
}
|
||||
|
||||
// invokePlugin writes plugin config to a temp file, then
|
||||
// passes the full temp file path as the first arg to a process
|
||||
// running the plugin binary. Process output is returned.
|
||||
func (p *ExecPlugin) invokePlugin(input []byte) ([]byte, error) {
|
||||
f, err := ioutil.TempFile("", tmpConfigFilePrefix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(
|
||||
err, "creating tmp plugin config file")
|
||||
}
|
||||
_, err = f.Write(p.cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(
|
||||
err, "writing plugin config to "+f.Name())
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(
|
||||
err, "closing plugin config file "+f.Name())
|
||||
}
|
||||
cmd := exec.Command(
|
||||
p.path, append([]string{f.Name()}, p.args...)...)
|
||||
cmd.Env = p.getEnv()
|
||||
cmd.Stdin = bytes.NewReader(input)
|
||||
cmd.Stderr = os.Stderr
|
||||
if _, err := os.Stat(p.h.Loader().Root()); err == nil {
|
||||
cmd.Dir = p.h.Loader().Root()
|
||||
}
|
||||
result, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err, "failure in plugin configured via %s; %v",
|
||||
f.Name(), err.Error())
|
||||
}
|
||||
return result, os.Remove(f.Name())
|
||||
}
|
||||
|
||||
func (p *ExecPlugin) getEnv() []string {
|
||||
env := os.Environ()
|
||||
env = append(env,
|
||||
"KUSTOMIZE_PLUGIN_CONFIG_STRING="+string(p.cfg),
|
||||
"KUSTOMIZE_PLUGIN_CONFIG_ROOT="+p.h.Loader().Root())
|
||||
return env
|
||||
}
|
||||
|
||||
// Returns a new copy of the given ResMap with the ResIds annotated in each Resource
|
||||
func (p *ExecPlugin) getResMapWithIdAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
|
||||
inputRM := rm.DeepCopy()
|
||||
for _, r := range inputRM.Resources() {
|
||||
idString, err := yaml.Marshal(r.CurId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
annotations := r.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
annotations[idAnnotation] = string(idString)
|
||||
r.SetAnnotations(annotations)
|
||||
}
|
||||
return inputRM, nil
|
||||
}
|
||||
|
||||
// updateResMapValues updates the Resource value in the given ResMap
|
||||
// with the emitted Resource values in output.
|
||||
func (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error {
|
||||
outputRM, err := p.h.ResmapFactory().NewResMapFromBytes(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, r := range outputRM.Resources() {
|
||||
// for each emitted Resource, find the matching Resource in the original ResMap
|
||||
// using its id
|
||||
annotations := r.GetAnnotations()
|
||||
idString, ok := annotations[idAnnotation]
|
||||
if !ok {
|
||||
return fmt.Errorf("the transformer %s should not remove annotation %s",
|
||||
p.path, idAnnotation)
|
||||
}
|
||||
id := resid.ResId{}
|
||||
err := yaml.Unmarshal([]byte(idString), &id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := rm.GetByCurrentId(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find unique match to %s", id.String())
|
||||
}
|
||||
// remove the annotation set by Kustomize to track the resource
|
||||
delete(annotations, idAnnotation)
|
||||
if len(annotations) == 0 {
|
||||
annotations = nil
|
||||
}
|
||||
r.SetAnnotations(annotations)
|
||||
|
||||
// update the ResMap resource value with the transformed object
|
||||
res.Kunstructured = r.Kunstructured
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateResourceOptions updates the generator options for each resource in the
|
||||
// given ResMap based on plugin provided annotations.
|
||||
func (p *ExecPlugin) UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
|
||||
for _, r := range rm.Resources() {
|
||||
// Disable name hashing by default and require plugin to explicitly
|
||||
// request it for each resource.
|
||||
annotations := r.GetAnnotations()
|
||||
behavior := annotations[BehaviorAnnotation]
|
||||
var needsHash bool
|
||||
if val, ok := annotations[HashAnnotation]; ok {
|
||||
b, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"the annotation %q contains an invalid value (%q)",
|
||||
HashAnnotation, val)
|
||||
}
|
||||
needsHash = b
|
||||
}
|
||||
delete(annotations, HashAnnotation)
|
||||
delete(annotations, BehaviorAnnotation)
|
||||
if len(annotations) == 0 {
|
||||
annotations = nil
|
||||
}
|
||||
r.SetAnnotations(annotations)
|
||||
r.SetOptions(types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: behavior},
|
||||
&types.GeneratorOptions{DisableNameSuffixHash: !needsHash}))
|
||||
}
|
||||
return rm, nil
|
||||
}
|
||||
179
api/plugins/execplugin/execplugin_test.go
Normal file
179
api/plugins/execplugin/execplugin_test.go
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package execplugin_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/config"
|
||||
. "sigs.k8s.io/kustomize/v3/api/plugins/execplugin"
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/v3/api/resmap"
|
||||
"sigs.k8s.io/kustomize/v3/api/resource"
|
||||
"sigs.k8s.io/kustomize/v3/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/v3/api/types"
|
||||
"sigs.k8s.io/kustomize/v3/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
|
||||
)
|
||||
|
||||
func TestExecPluginConfig(t *testing.T) {
|
||||
path := "/app"
|
||||
rf := resmap.NewFactory(
|
||||
resource.NewFactory(
|
||||
kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||
ldr := loadertest.NewFakeLoader(path)
|
||||
v := valtest_test.MakeFakeValidator()
|
||||
pluginConfig := rf.RF().FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "someteam.example.com/v1",
|
||||
"kind": "SedTransformer",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "some-random-name",
|
||||
},
|
||||
"argsOneLiner": "one two",
|
||||
"argsFromFile": "sed-input.txt",
|
||||
})
|
||||
|
||||
ldr.AddFile("/app/sed-input.txt", []byte(`
|
||||
s/$FOO/foo/g
|
||||
s/$BAR/bar/g
|
||||
\ \ \
|
||||
`))
|
||||
|
||||
p := NewExecPlugin(
|
||||
loader.AbsolutePluginPath(
|
||||
config.DefaultPluginConfig(),
|
||||
pluginConfig.OrgId()))
|
||||
|
||||
yaml, err := pluginConfig.AsYAML()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
p.Config(resmap.NewPluginHelpers(ldr, v, rf), yaml)
|
||||
|
||||
expected := "/kustomize/plugin/someteam.example.com/v1/sedtransformer/SedTransformer"
|
||||
if !strings.HasSuffix(p.Path(), expected) {
|
||||
t.Fatalf("expected suffix '%s', got '%s'", expected, p.Path())
|
||||
}
|
||||
|
||||
expected = `apiVersion: someteam.example.com/v1
|
||||
argsFromFile: sed-input.txt
|
||||
argsOneLiner: one two
|
||||
kind: SedTransformer
|
||||
metadata:
|
||||
name: some-random-name
|
||||
`
|
||||
if expected != string(p.Cfg()) {
|
||||
t.Fatalf("expected cfg '%s', got '%s'", expected, string(p.Cfg()))
|
||||
|
||||
}
|
||||
if len(p.Args()) != 5 {
|
||||
t.Fatalf("unexpected arg len %d, %v", len(p.Args()), p.Args())
|
||||
}
|
||||
if p.Args()[0] != "one" ||
|
||||
p.Args()[1] != "two" ||
|
||||
p.Args()[2] != "s/$FOO/foo/g" ||
|
||||
p.Args()[3] != "s/$BAR/bar/g" ||
|
||||
p.Args()[4] != "\\ \\ \\ " {
|
||||
t.Fatalf("unexpected arg array: %v", p.Args())
|
||||
}
|
||||
}
|
||||
|
||||
func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource {
|
||||
r := rf.FromMap(map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{"name": name},
|
||||
})
|
||||
annotations := map[string]string{}
|
||||
if behavior != "" {
|
||||
annotations[BehaviorAnnotation] = behavior
|
||||
}
|
||||
if hashValue != nil {
|
||||
annotations[HashAnnotation] = *hashValue
|
||||
}
|
||||
if len(annotations) > 0 {
|
||||
r.SetAnnotations(annotations)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource {
|
||||
return rf.FromMapAndOption(map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{"name": name},
|
||||
}, &types.GeneratorArgs{Behavior: behavior}, &types.GeneratorOptions{DisableNameSuffixHash: disableHash})
|
||||
}
|
||||
|
||||
func strptr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestUpdateResourceOptions(t *testing.T) {
|
||||
p := NewExecPlugin("")
|
||||
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||
in := resmap.New()
|
||||
expected := resmap.New()
|
||||
cases := []struct {
|
||||
behavior string
|
||||
needsHash bool
|
||||
hashValue *string
|
||||
}{
|
||||
{hashValue: strptr("false")},
|
||||
{hashValue: strptr("true"), needsHash: true},
|
||||
{behavior: "replace"},
|
||||
{behavior: "merge"},
|
||||
{behavior: "create"},
|
||||
{behavior: "nonsense"},
|
||||
{behavior: "merge", hashValue: strptr("false")},
|
||||
{behavior: "merge", hashValue: strptr("true"), needsHash: true},
|
||||
}
|
||||
for i, c := range cases {
|
||||
name := fmt.Sprintf("test%d", i)
|
||||
in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue))
|
||||
expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash))
|
||||
}
|
||||
actual, err := p.UpdateResourceOptions(in)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err.Error())
|
||||
}
|
||||
for i, a := range expected.Resources() {
|
||||
b := actual.GetByIndex(i)
|
||||
if b == nil {
|
||||
t.Fatalf("resource %d missing from processed map", i)
|
||||
}
|
||||
if !a.Equals(b) {
|
||||
t.Errorf("expected %v got %v", a, b)
|
||||
}
|
||||
if a.NeedHashSuffix() != b.NeedHashSuffix() {
|
||||
t.Errorf("")
|
||||
}
|
||||
if a.Behavior() != b.Behavior() {
|
||||
t.Errorf("expected %v got %v", a.Behavior(), b.Behavior())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) {
|
||||
p := NewExecPlugin("")
|
||||
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||
cases := []string{
|
||||
"",
|
||||
"FaLsE",
|
||||
"TrUe",
|
||||
"potato",
|
||||
}
|
||||
for i, c := range cases {
|
||||
name := fmt.Sprintf("test%d", i)
|
||||
in := resmap.New()
|
||||
in.Append(makeConfigMap(rf, name, "", &c))
|
||||
_, err := p.UpdateResourceOptions(in)
|
||||
if err == nil {
|
||||
t.Errorf("expected error from value %q", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
199
api/plugins/loader/loader.go
Normal file
199
api/plugins/loader/loader.go
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"plugin"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/builtins"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/v3/api/ifc"
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/config"
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/execplugin"
|
||||
"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/kustomize/v3/pkg/plugins"
|
||||
)
|
||||
|
||||
type Loader struct {
|
||||
pc *types.PluginConfig
|
||||
rf *resmap.Factory
|
||||
}
|
||||
|
||||
func NewLoader(
|
||||
pc *types.PluginConfig, rf *resmap.Factory) *Loader {
|
||||
return &Loader{pc: pc, rf: rf}
|
||||
}
|
||||
|
||||
func (l *Loader) LoadGenerators(
|
||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
||||
var result []resmap.Generator
|
||||
for _, res := range rm.Resources() {
|
||||
g, err := l.LoadGenerator(ldr, v, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, g)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *Loader) LoadGenerator(
|
||||
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (resmap.Generator, error) {
|
||||
c, err := l.loadAndConfigurePlugin(ldr, v, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, ok := c.(resmap.Generator)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("plugin %s not a generator", res.OrgId())
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (l *Loader) LoadTransformers(
|
||||
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Transformer, error) {
|
||||
var result []resmap.Transformer
|
||||
for _, res := range rm.Resources() {
|
||||
t, err := l.LoadTransformer(ldr, v, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, t)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (l *Loader) LoadTransformer(
|
||||
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (resmap.Transformer, error) {
|
||||
c, err := l.loadAndConfigurePlugin(ldr, v, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t, ok := c.(resmap.Transformer)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("plugin %s not a transformer", res.OrgId())
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func relativePluginPath(id resid.ResId) string {
|
||||
return filepath.Join(
|
||||
id.Group,
|
||||
id.Version,
|
||||
strings.ToLower(id.Kind))
|
||||
}
|
||||
|
||||
func AbsolutePluginPath(pc *types.PluginConfig, id resid.ResId) string {
|
||||
return filepath.Join(
|
||||
pc.DirectoryPath, relativePluginPath(id), id.Kind)
|
||||
}
|
||||
|
||||
func (l *Loader) absolutePluginPath(id resid.ResId) string {
|
||||
return AbsolutePluginPath(l.pc, id)
|
||||
}
|
||||
|
||||
func isBuiltinPlugin(res *resource.Resource) bool {
|
||||
// TODO: the special string should appear in Group, not Version.
|
||||
return res.GetGvk().Group == "" &&
|
||||
res.GetGvk().Version == config.BuiltinPluginApiVersion
|
||||
}
|
||||
|
||||
func (l *Loader) loadAndConfigurePlugin(
|
||||
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (c resmap.Configurable, err error) {
|
||||
if isBuiltinPlugin(res) {
|
||||
// Instead of looking for and loading a .so file, just
|
||||
// instantiate the plugin from a generated factory
|
||||
// function (see "pluginator"). Being able to do this
|
||||
// is what makes a plugin "builtin".
|
||||
c, err = l.makeBuiltinPlugin(res.GetGvk())
|
||||
} else if l.pc.Enabled {
|
||||
c, err = l.loadPlugin(res.OrgId())
|
||||
} else {
|
||||
err = config.NotEnabledErr(res.OrgId().Kind)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
yaml, err := res.AsYAML()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "marshalling yaml from res %s", res.OrgId())
|
||||
}
|
||||
err = c.Config(resmap.NewPluginHelpers(ldr, v, l.rf), yaml)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err, "plugin %s fails configuration", res.OrgId())
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) {
|
||||
bpt := builtins.GetBuiltinPluginType(r.Kind)
|
||||
if f, ok := builtins.GeneratorFactories[bpt]; ok {
|
||||
return f(), nil
|
||||
}
|
||||
if f, ok := builtins.TransformerFactories[bpt]; ok {
|
||||
return f(), nil
|
||||
}
|
||||
return nil, errors.Errorf("unable to load builtin %s", r)
|
||||
}
|
||||
|
||||
func (l *Loader) loadPlugin(resId resid.ResId) (resmap.Configurable, error) {
|
||||
p := execplugin.NewExecPlugin(l.absolutePluginPath(resId))
|
||||
if p.IsAvailable() {
|
||||
return p, nil
|
||||
}
|
||||
c, err := l.loadGoPlugin(resId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// registry is a means to avoid trying to load the same .so file
|
||||
// into memory more than once, which results in an error.
|
||||
// Each test makes its own loader, and tries to load its own plugins,
|
||||
// but the loaded .so files are in shared memory, so one will get
|
||||
// "this plugin already loaded" errors if the registry is maintained
|
||||
// as a Loader instance variable. So make it a package variable.
|
||||
var registry = make(map[string]resmap.Configurable)
|
||||
|
||||
func (l *Loader) loadGoPlugin(id resid.ResId) (resmap.Configurable, error) {
|
||||
regId := relativePluginPath(id)
|
||||
if c, ok := registry[regId]; ok {
|
||||
return copyPlugin(c), nil
|
||||
}
|
||||
absPath := l.absolutePluginPath(id)
|
||||
p, err := plugin.Open(absPath + ".so")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "plugin %s fails to load", absPath)
|
||||
}
|
||||
symbol, err := p.Lookup(plugins.PluginSymbol)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
err, "plugin %s doesn't have symbol %s",
|
||||
regId, plugins.PluginSymbol)
|
||||
}
|
||||
c, ok := symbol.(resmap.Configurable)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("plugin %s not configurable", regId)
|
||||
}
|
||||
registry[regId] = c
|
||||
return copyPlugin(c), nil
|
||||
}
|
||||
|
||||
func copyPlugin(c resmap.Configurable) resmap.Configurable {
|
||||
indirect := reflect.Indirect(reflect.ValueOf(c))
|
||||
newIndirect := reflect.New(indirect.Type())
|
||||
newIndirect.Elem().Set(reflect.ValueOf(indirect.Interface()))
|
||||
newNamed := newIndirect.Interface()
|
||||
return newNamed.(resmap.Configurable)
|
||||
}
|
||||
75
api/plugins/loader/loader_test.go
Normal file
75
api/plugins/loader/loader_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package loader_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/v3/api/plugins/config"
|
||||
. "sigs.k8s.io/kustomize/v3/api/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/v3/api/resmap"
|
||||
"sigs.k8s.io/kustomize/v3/api/resource"
|
||||
"sigs.k8s.io/kustomize/v3/api/testutils/kusttest"
|
||||
"sigs.k8s.io/kustomize/v3/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/v3/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
|
||||
)
|
||||
|
||||
const (
|
||||
secretGenerator = `
|
||||
apiVersion: builtin
|
||||
kind: SecretGenerator
|
||||
metadata:
|
||||
name: secretGenerator
|
||||
name: mySecret
|
||||
behavior: merge
|
||||
envFiles:
|
||||
- a.env
|
||||
- b.env
|
||||
valueFiles:
|
||||
- longsecret.txt
|
||||
literals:
|
||||
- FRUIT=apple
|
||||
- VEGETABLE=carrot
|
||||
`
|
||||
someServiceGenerator = `
|
||||
apiVersion: someteam.example.com/v1
|
||||
kind: SomeServiceGenerator
|
||||
metadata:
|
||||
name: myServiceGenerator
|
||||
service: my-service
|
||||
port: "12345"
|
||||
`
|
||||
)
|
||||
|
||||
func TestLoader(t *testing.T) {
|
||||
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||
defer tc.Reset()
|
||||
|
||||
tc.BuildGoPlugin(
|
||||
"builtin", "", "SecretGenerator")
|
||||
tc.BuildGoPlugin(
|
||||
"someteam.example.com", "v1", "SomeServiceGenerator")
|
||||
|
||||
rmF := resmap.NewFactory(resource.NewFactory(
|
||||
kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||
|
||||
ldr := loadertest.NewFakeLoader("/foo")
|
||||
|
||||
pLdr := NewLoader(config.ActivePluginConfig(), rmF)
|
||||
if pLdr == nil {
|
||||
t.Fatal("expect non-nil loader")
|
||||
}
|
||||
|
||||
m, err := rmF.NewResMapFromBytes([]byte(
|
||||
someServiceGenerator + "---\n" + secretGenerator))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = pLdr.LoadGenerators(ldr, valtest_test.MakeFakeValidator(), m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user