add transformer plugins

This commit is contained in:
Jingfang Liu
2019-04-04 16:48:29 -07:00
parent 440d036176
commit 4f1a2350ce
9 changed files with 172 additions and 69 deletions

View File

@@ -62,7 +62,8 @@ url examples:
func NewCmdBuild(
out io.Writer, fs fs.FileSystem,
rf *resmap.Factory,
ptf transformer.Factory) *cobra.Command {
ptf transformer.Factory,
b bool) *cobra.Command {
var o Options
cmd := &cobra.Command{
@@ -75,7 +76,7 @@ func NewCmdBuild(
if err != nil {
return err
}
return o.RunBuild(out, fs, rf, ptf)
return o.RunBuild(out, fs, rf, ptf, b)
},
}
cmd.Flags().StringVarP(
@@ -102,13 +103,14 @@ func (o *Options) Validate(args []string) error {
// RunBuild runs build command.
func (o *Options) RunBuild(
out io.Writer, fSys fs.FileSystem,
rf *resmap.Factory, ptf transformer.Factory) error {
rf *resmap.Factory, ptf transformer.Factory,
b bool) error {
ldr, err := loader.NewLoader(o.kustomizationPath, fSys)
if err != nil {
return err
}
defer ldr.Cleanup()
kt, err := target.NewKustTarget(ldr, rf, ptf)
kt, err := target.NewKustTarget(ldr, rf, ptf, b)
if err != nil {
return err
}

View File

@@ -68,7 +68,7 @@ See https://sigs.k8s.io/kustomize
build.NewCmdBuild(
stdOut, fSys,
resmap.NewFactory(resource.NewFactory(uf)),
transformer.NewFactoryImpl()),
transformer.NewFactoryImpl(), genMetaArgs.PluginConfig.GoEnabled),
edit.NewCmdEdit(fSys, validator.NewKustValidator(), uf),
misc.NewCmdConfig(fSys),
misc.NewCmdVersion(stdOut),

View File

@@ -28,5 +28,6 @@ var KustomizationFileNames = []string{
}
const (
PgmName = "kustomize"
PgmName = "kustomize"
PluginsDir = "plugins"
)

View File

@@ -0,0 +1,95 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package plugins
import (
"fmt"
"path/filepath"
"plugin"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers"
)
const transformerSymbol = "Transformer"
type Configurable interface {
Config(k ifc.Kunstructured) error
}
type transformerLoader struct {
pluginDir string
enabled bool
}
func NewTransformerLoader(b bool) transformerLoader {
return transformerLoader{
pluginDir: filepath.Join(pgmconfig.ConfigRoot(), pgmconfig.PluginsDir),
enabled: b,
}
}
func (l transformerLoader) Load(rm resmap.ResMap) ([]transformers.Transformer, error) {
if len(rm) == 0 {
return nil, nil
}
if !l.enabled {
return nil, fmt.Errorf("plugin is not enabled")
}
var result []transformers.Transformer
for id, res := range rm {
t, err := l.load(id, res)
if err != nil {
return nil, err
}
result = append(result, t)
}
return result, nil
}
func (l transformerLoader) load(id resid.ResId, res *resource.Resource) (transformers.Transformer, error) {
fileName := filepath.Join(l.pluginDir, id.Gvk().Kind+".so")
goPlugin, err := plugin.Open(fileName)
if err != nil {
return nil, fmt.Errorf("plugin %s file not opened", fileName)
}
symbol, err := goPlugin.Lookup(transformerSymbol)
if err != nil {
return nil, fmt.Errorf("plugin %s fails lookup", fileName)
}
c, ok := symbol.(Configurable)
if !ok {
return nil, fmt.Errorf("plugin %s not configurable", fileName)
}
err = c.Config(res)
if err != nil {
return nil, errors.Wrapf(err, "plugin %s fails configuration", fileName)
}
t, ok := c.(transformers.Transformer)
if !ok {
return nil, fmt.Errorf("plugin %s not a transformer", fileName)
}
return t, nil
}

View File

@@ -21,9 +21,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"plugin"
"strings"
"github.com/ghodss/yaml"
@@ -34,6 +31,7 @@ import (
interror "sigs.k8s.io/kustomize/pkg/internal/error"
patchtransformer "sigs.k8s.io/kustomize/pkg/patch/transformer"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
"sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers"
@@ -43,17 +41,19 @@ import (
// KustTarget encapsulates the entirety of a kustomization build.
type KustTarget struct {
kustomization *types.Kustomization
ldr ifc.Loader
rFactory *resmap.Factory
tFactory transformer.Factory
kustomization *types.Kustomization
ldr ifc.Loader
rFactory *resmap.Factory
tFactory transformer.Factory
goPluginEnabled bool
}
// NewKustTarget returns a new instance of KustTarget primed with a Loader.
func NewKustTarget(
ldr ifc.Loader,
rFactory *resmap.Factory,
tFactory transformer.Factory) (*KustTarget, error) {
tFactory transformer.Factory,
b bool) (*KustTarget, error) {
content, err := loadKustFile(ldr)
if err != nil {
return nil, err
@@ -71,10 +71,11 @@ func NewKustTarget(
strings.Join(errs, "\n"), ldr.Root())
}
return &KustTarget{
kustomization: &k,
ldr: ldr,
rFactory: rFactory,
tFactory: tFactory,
kustomization: &k,
ldr: ldr,
rFactory: rFactory,
tFactory: tFactory,
goPluginEnabled: b,
}, nil
}
@@ -255,7 +256,7 @@ func (kt *KustTarget) accumulateBases() (
continue
}
subKt, err := NewKustTarget(
ldr, kt.rFactory, kt.tFactory)
ldr, kt.rFactory, kt.tFactory, kt.goPluginEnabled)
if err != nil {
errs.Append(errors.Wrap(err, "couldn't make target for "+path))
ldr.Cleanup()
@@ -331,48 +332,11 @@ func (kt *KustTarget) newTransformer(
}
func (kt *KustTarget) loadTransformerPlugins() ([]transformers.Transformer, error) {
var result []transformers.Transformer
pc := types.PluginConfig{
DirectoryPath: os.Getenv("GOPATH"),
GoEnabled: true}
root := filepath.Join(
pc.DirectoryPath, "src", "sigs.k8s.io", "kustomize", "plugins")
if !pc.GoEnabled {
return nil, fmt.Errorf("plugins not enabled")
}
transformerPluginConfigs, err := kt.rFactory.FromFiles(
kt.ldr, kt.kustomization.Transformers)
for id, res := range transformerPluginConfigs {
fileName := filepath.Join(root, id.Gvk().Kind+".so")
goPlugin, err := plugin.Open(fileName)
if err != nil {
return nil, fmt.Errorf("plugin %s file not opened", fileName)
}
symbol, err := goPlugin.Lookup("Transformer")
if err != nil {
return nil, fmt.Errorf("plugin %s fails lookup", fileName)
}
c, ok := symbol.(transformers.Configurable)
if !ok {
return nil, fmt.Errorf("plugin %s not configurable", fileName)
}
err = c.Config(res)
if err != nil {
return nil, errors.Wrapf(err, "plugin %s fails configuration", fileName)
}
t, ok := c.(transformers.Transformer)
if !ok {
return nil, fmt.Errorf("plugin %s not a transformer", fileName)
}
result = append(result, t)
fmt.Printf("Added plugin %s\n", fileName)
if err != nil {
return nil, err
}
return result, err
tl := plugins.NewTransformerLoader(kt.goPluginEnabled)
return tl.Load(transformerPluginConfigs)
}

View File

@@ -204,7 +204,7 @@ func TestResources(t *testing.T) {
}
func TestKustomizationNotFound(t *testing.T) {
_, err := NewKustTarget(loadertest.NewFakeLoader("/foo"), nil, nil)
_, err := NewKustTarget(loadertest.NewFakeLoader("/foo"), nil, nil, false)
if err == nil {
t.Fatalf("expected an error")
}

View File

@@ -40,6 +40,7 @@ type KustTestHarness struct {
t *testing.T
rf *resmap.Factory
ldr loadertest.FakeLoader
b bool
}
func NewKustTestHarness(t *testing.T, path string) *KustTestHarness {
@@ -55,12 +56,13 @@ func NewKustTestHarnessWithPluginConfig(
rf: resmap.NewFactory(resource.NewFactory(
kunstruct.NewKunstructuredFactoryWithGeneratorArgs(
&types.GeneratorMetaArgs{PluginConfig: config}))),
ldr: loadertest.NewFakeLoader(path)}
ldr: loadertest.NewFakeLoader(path),
b: config.GoEnabled}
}
func (th *KustTestHarness) makeKustTarget() *KustTarget {
kt, err := NewKustTarget(
th.ldr, th.rf, transformer.NewFactoryImpl())
th.ldr, th.rf, transformer.NewFactoryImpl(), th.b)
if err != nil {
th.t.Fatalf("Unexpected construction error %v", err)
}

View File

@@ -14,8 +14,14 @@ limitations under the License.
package target_test
import (
"sigs.k8s.io/kustomize/pkg/types"
"os"
"os/exec"
"path/filepath"
"testing"
"fmt"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/types"
)
func writeDeployment(th *KustTestHarness, path string) {
@@ -55,7 +61,45 @@ metadata:
`)
}
func buildGoPlugins(dir, filename string) error {
commands := []string{
"build",
"-buildmode",
"plugin",
"-tags=plugin",
"-o",
filename + ".so",
filename + ".go",
}
goBin := filepath.Join(os.Getenv("GOROOT"), "bin", "go")
if _, err := os.Stat(goBin); err != nil {
return fmt.Errorf("go binary not found %s", goBin)
}
cmd := exec.Command(goBin, commands...)
cmd.Env = os.Environ()
cmd.Dir = filepath.Join(dir, "kustomize", "plugins")
return cmd.Run()
}
func TestOrderedTransformers(t *testing.T) {
dir, err := filepath.Abs("../../..")
if err != nil {
t.Errorf("unexpected error %v", err)
}
os.Setenv(pgmconfig.XDG_CONFIG_HOME, dir)
defer os.Unsetenv(pgmconfig.XDG_CONFIG_HOME)
err = buildGoPlugins(dir, "StringPrefixer")
if err != nil {
t.Errorf("unexpected error %v", err)
}
err = buildGoPlugins(dir, "DatePrefixer")
if err != nil {
t.Errorf("unexpected error %v", err)
}
th := NewKustTestHarnessWithPluginConfig(
t, "/app", types.PluginConfig{GoEnabled: true})
th.writeK("/app", `

View File

@@ -18,7 +18,6 @@ limitations under the License.
package transformers
import (
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resmap"
)
@@ -27,7 +26,3 @@ type Transformer interface {
// Transform modifies data in the argument, e.g. adding labels to resources that can be labelled.
Transform(m resmap.ResMap) error
}
type Configurable interface {
Config(k ifc.Kunstructured) error
}