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

View File

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

View File

@@ -29,4 +29,5 @@ var KustomizationFileNames = []string{
const ( 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" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"path/filepath"
"plugin"
"strings" "strings"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
@@ -34,6 +31,7 @@ import (
interror "sigs.k8s.io/kustomize/pkg/internal/error" interror "sigs.k8s.io/kustomize/pkg/internal/error"
patchtransformer "sigs.k8s.io/kustomize/pkg/patch/transformer" patchtransformer "sigs.k8s.io/kustomize/pkg/patch/transformer"
"sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers" "sigs.k8s.io/kustomize/pkg/transformers"
@@ -47,13 +45,15 @@ type KustTarget struct {
ldr ifc.Loader ldr ifc.Loader
rFactory *resmap.Factory rFactory *resmap.Factory
tFactory transformer.Factory tFactory transformer.Factory
goPluginEnabled bool
} }
// NewKustTarget returns a new instance of KustTarget primed with a Loader. // NewKustTarget returns a new instance of KustTarget primed with a Loader.
func NewKustTarget( func NewKustTarget(
ldr ifc.Loader, ldr ifc.Loader,
rFactory *resmap.Factory, rFactory *resmap.Factory,
tFactory transformer.Factory) (*KustTarget, error) { tFactory transformer.Factory,
b bool) (*KustTarget, error) {
content, err := loadKustFile(ldr) content, err := loadKustFile(ldr)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -75,6 +75,7 @@ func NewKustTarget(
ldr: ldr, ldr: ldr,
rFactory: rFactory, rFactory: rFactory,
tFactory: tFactory, tFactory: tFactory,
goPluginEnabled: b,
}, nil }, nil
} }
@@ -255,7 +256,7 @@ func (kt *KustTarget) accumulateBases() (
continue continue
} }
subKt, err := NewKustTarget( subKt, err := NewKustTarget(
ldr, kt.rFactory, kt.tFactory) ldr, kt.rFactory, kt.tFactory, kt.goPluginEnabled)
if err != nil { if err != nil {
errs.Append(errors.Wrap(err, "couldn't make target for "+path)) errs.Append(errors.Wrap(err, "couldn't make target for "+path))
ldr.Cleanup() ldr.Cleanup()
@@ -331,48 +332,11 @@ func (kt *KustTarget) newTransformer(
} }
func (kt *KustTarget) loadTransformerPlugins() ([]transformers.Transformer, error) { 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( transformerPluginConfigs, err := kt.rFactory.FromFiles(
kt.ldr, kt.kustomization.Transformers) 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 { if err != nil {
return nil, fmt.Errorf("plugin %s file not opened", fileName) return nil, err
} }
symbol, err := goPlugin.Lookup("Transformer") tl := plugins.NewTransformerLoader(kt.goPluginEnabled)
if err != nil { return tl.Load(transformerPluginConfigs)
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)
}
return result, err
} }

View File

@@ -204,7 +204,7 @@ func TestResources(t *testing.T) {
} }
func TestKustomizationNotFound(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 { if err == nil {
t.Fatalf("expected an error") t.Fatalf("expected an error")
} }

View File

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

View File

@@ -14,8 +14,14 @@ limitations under the License.
package target_test package target_test
import ( import (
"sigs.k8s.io/kustomize/pkg/types" "os"
"os/exec"
"path/filepath"
"testing" "testing"
"fmt"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/types"
) )
func writeDeployment(th *KustTestHarness, path string) { 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) { 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( th := NewKustTestHarnessWithPluginConfig(
t, "/app", types.PluginConfig{GoEnabled: true}) t, "/app", types.PluginConfig{GoEnabled: true})
th.writeK("/app", ` th.writeK("/app", `

View File

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