diff --git a/k8sdeps/kv/plugin/registry.go b/k8sdeps/kv/plugin/registry.go index 6d4f407cc..342692725 100644 --- a/k8sdeps/kv/plugin/registry.go +++ b/k8sdeps/kv/plugin/registry.go @@ -32,8 +32,8 @@ type Registry struct { } const ( - TransformerSymbol = "Transformer" - PluginsDir = "plugins" + PluginSymbol = "KustomizePlugin" + PluginRoot = "plugins" pluginTypeGo = types.PluginType("go") pluginTypeBuiltIn = types.PluginType("builtin") ) @@ -48,7 +48,7 @@ func DefaultPluginConfig() *types.PluginConfig { return &types.PluginConfig{ GoEnabled: false, DirectoryPath: filepath.Join( - pgmconfig.ConfigRoot(), PluginsDir), + pgmconfig.ConfigRoot(), PluginRoot), } } diff --git a/pkg/plugins/generators.go b/pkg/plugins/generators.go index ab704b0ac..45c23e2e8 100644 --- a/pkg/plugins/generators.go +++ b/pkg/plugins/generators.go @@ -18,81 +18,40 @@ package plugins import ( "fmt" - "path/filepath" - "plugin" + "sigs.k8s.io/kustomize/pkg/transformers" + "sigs.k8s.io/kustomize/pkg/types" - "github.com/pkg/errors" - "sigs.k8s.io/kustomize/pkg/pgmconfig" - "sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/resource" ) -const generatorSymbol = "Generator" - -type Generatable interface { - Generate() (resmap.ResMap, error) -} - type generatorLoader struct { - pluginDir string - enabled bool - rf *resmap.Factory + pc *types.PluginConfig } -func NewGeneratorLoader(b bool, f *resmap.Factory) generatorLoader { - return generatorLoader{ - pluginDir: filepath.Join(pgmconfig.ConfigRoot(), pgmconfig.PluginsDir), - enabled: b, - rf: f, - } +func NewGeneratorLoader(pc *types.PluginConfig) generatorLoader { + return generatorLoader{pc: pc} } -func (l generatorLoader) Load(rm resmap.ResMap) (resmap.ResMap, error) { +func (l generatorLoader) Load( + rm resmap.ResMap) ([]transformers.Generator, error) { if len(rm) == 0 { return nil, nil } - if !l.enabled { - return nil, fmt.Errorf("plugin is not enabled") + if !l.pc.GoEnabled { + return nil, fmt.Errorf("plugins not enabled") } - var result resmap.ResMap + var result []transformers.Generator for id, res := range rm { - r, err := l.load(id, res) + fileName := pluginFileName(l.pc, id) + c, err := loadAndConfigurePlugin(fileName, res) if err != nil { return nil, err } - result, err = resmap.MergeWithErrorOnIdCollision(result, r) - if err != nil { - return nil, err + g, ok := c.(transformers.Generator) + if !ok { + return nil, fmt.Errorf("plugin %s not a generator", fileName) } + result = append(result, g) } return result, nil } - -func (l generatorLoader) load(id resid.ResId, res *resource.Resource) (resmap.ResMap, 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(generatorSymbol) - 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) - } - - g, ok := c.(Generatable) - if !ok { - return nil, fmt.Errorf("plugin %s not a transformer", fileName) - } - return g.Generate() -} diff --git a/pkg/plugins/transformers.go b/pkg/plugins/transformers.go index de962acc5..b6cf28864 100644 --- a/pkg/plugins/transformers.go +++ b/pkg/plugins/transformers.go @@ -43,7 +43,8 @@ func NewTransformerLoader(pc *types.PluginConfig) transformerLoader { return transformerLoader{pc: pc} } -func (l transformerLoader) Load(rm resmap.ResMap) ([]transformers.Transformer, error) { +func (l transformerLoader) Load( + rm resmap.ResMap) ([]transformers.Transformer, error) { if len(rm) == 0 { return nil, nil } @@ -52,30 +53,37 @@ func (l transformerLoader) Load(rm resmap.ResMap) ([]transformers.Transformer, e } var result []transformers.Transformer for id, res := range rm { - t, err := l.load(id, res) + fileName := pluginFileName(l.pc, id) + c, err := loadAndConfigurePlugin(fileName, res) if err != nil { return nil, err } + t, ok := c.(transformers.Transformer) + if !ok { + return nil, fmt.Errorf("plugin %s not a transformer", fileName) + } result = append(result, t) } return result, nil } -func (l transformerLoader) load( - id resid.ResId, res *resource.Resource) (transformers.Transformer, error) { - fileName := filepath.Join( - l.pc.DirectoryPath, +func pluginFileName(pc *types.PluginConfig, id resid.ResId) string { + return filepath.Join( + pc.DirectoryPath, id.Gvk().Group, id.Gvk().Version, id.Gvk().Kind+".so") +} + +func loadAndConfigurePlugin( + fileName string, res *resource.Resource) (Configurable, error) { goPlugin, err := plugin.Open(fileName) if err != nil { return nil, fmt.Errorf("plugin %s file not opened", fileName) } - - symbol, err := goPlugin.Lookup(kplugin.TransformerSymbol) + symbol, err := goPlugin.Lookup(kplugin.PluginSymbol) if err != nil { - return nil, fmt.Errorf("plugin %s fails lookup", fileName) + return nil, fmt.Errorf( + "plugin %s doesn't have symbol %s", fileName, kplugin.PluginSymbol) } - c, ok := symbol.(Configurable) if !ok { return nil, fmt.Errorf("plugin %s not configurable", fileName) @@ -84,10 +92,5 @@ func (l transformerLoader) load( 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 + return c, nil } diff --git a/pkg/target/generatorplugin_test.go b/pkg/target/generatorplugin_test.go index 23bd47f15..a0d3b5140 100644 --- a/pkg/target/generatorplugin_test.go +++ b/pkg/target/generatorplugin_test.go @@ -14,17 +14,14 @@ limitations under the License. package target_test import ( - "os" - "path/filepath" "testing" - "sigs.k8s.io/kustomize/pkg/pgmconfig" - "sigs.k8s.io/kustomize/pkg/types" + "sigs.k8s.io/kustomize/k8sdeps/kv/plugin" ) func writeGenerator(th *KustTestHarness, path string) { th.writeF(path, ` -apiVersion: strings.microwoosh.com/v1 +apiVersion: someteam.example.com/v1 kind: ServiceGenerator metadata: name: myServiceGenerator @@ -34,20 +31,14 @@ port: "12345" } func TestGeneratorPlugin(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) + tc := NewTestEnvController(t).Set() + defer tc.Reset() - err = buildGoPlugins(dir, "ServiceGenerator") - if err != nil { - t.Errorf("unexpected error %v", err) - } + tc.BuildGoPlugin( + "someteam.example.com", "v1", "ServiceGenerator") th := NewKustTestHarnessWithPluginConfig( - t, "/app", types.PluginConfig{GoEnabled: true}) + t, "/app", plugin.ActivePluginConfig()) th.writeK("/app", ` generators: - serviceGenerator.yaml diff --git a/pkg/target/kusttarget.go b/pkg/target/kusttarget.go index c01bf63ee..ac5589927 100644 --- a/pkg/target/kusttarget.go +++ b/pkg/target/kusttarget.go @@ -173,17 +173,6 @@ func (kt *KustTarget) AccumulateTarget() ( // nolint: gocyclo if err != nil { errs.Append(errors.Wrap(err, "MergeResourcesWithErrorOnIdCollision")) } - resourceFromGenerators, err := kt.loadGeneratorPlugins() - if err != nil { - errs.Append(errors.Wrap(err, "failed to load resources from generators")) - } - if len(errs.Get()) > 0 { - return ra, errs - } - err = ra.MergeResourcesWithErrorOnIdCollision(resourceFromGenerators) - if err != nil { - errs.Append(errors.Wrap(err, "MergeResourcesWithErrorOnIdCollision")) - } tConfig, err := config.MakeTransformerConfig( kt.ldr, kt.kustomization.Configurations) if err != nil { @@ -213,6 +202,12 @@ func (kt *KustTarget) AccumulateTarget() ( // nolint: gocyclo if err != nil { return nil, err } + if kt.pluginConfig.GoEnabled { + kt.generateFromPlugins(ra, errs) + if len(errs.Get()) > 0 { + return ra, errs + } + } patches, err := kt.rFactory.RF().SliceFromPatches( kt.ldr, kt.kustomization.PatchesStrategicMerge) if err != nil { @@ -232,6 +227,26 @@ func (kt *KustTarget) AccumulateTarget() ( // nolint: gocyclo return ra, nil } +func (kt *KustTarget) generateFromPlugins( + ra *accumulator.ResAccumulator, + errs *interror.KustomizationErrors) { + generators, err := kt.loadGeneratorPlugins() + if err != nil { + errs.Append(err) + } + for _, g := range generators { + resMap, err := g.Generate() + if err != nil { + errs.Append(err) + } else { + err = ra.MergeResourcesWithErrorOnIdCollision(resMap) + if err != nil { + errs.Append(errors.Wrap(err, "from plugin")) + } + } + } +} + func (kt *KustTarget) generateConfigMapsAndSecrets( errs *interror.KustomizationErrors) (resmap.ResMap, error) { cms, err := kt.rFactory.NewResMapFromConfigMapArgs( @@ -345,20 +360,19 @@ func (kt *KustTarget) newTransformer( } func (kt *KustTarget) loadTransformerPlugins() ([]transformers.Transformer, error) { - transformerPluginConfigs, err := kt.rFactory.FromFiles( + configs, err := kt.rFactory.FromFiles( kt.ldr, kt.kustomization.Transformers) if err != nil { return nil, err } - return plugins.NewTransformerLoader(kt.pluginConfig).Load(transformerPluginConfigs) + return plugins.NewTransformerLoader(kt.pluginConfig).Load(configs) } -func (kt *KustTarget) loadGeneratorPlugins() (resmap.ResMap, error) { - generatorPluginConfigs, err := kt.rFactory.FromFiles( +func (kt *KustTarget) loadGeneratorPlugins() ([]transformers.Generator, error) { + configs, err := kt.rFactory.FromFiles( kt.ldr, kt.kustomization.Generators) if err != nil { return nil, err } - gl := plugins.NewGeneratorLoader(kt.goPluginEnabled, kt.rFactory) - return gl.Load(generatorPluginConfigs) + return plugins.NewGeneratorLoader(kt.pluginConfig).Load(configs) } diff --git a/pkg/target/testenvcontroller_test.go b/pkg/target/testenvcontroller_test.go index 37d8dd223..bdf0bfad1 100644 --- a/pkg/target/testenvcontroller_test.go +++ b/pkg/target/testenvcontroller_test.go @@ -105,7 +105,7 @@ func (x *TestEnvController) BuildGoPlugin(plugin ...string) { // ObjectRoot is the objRoot dir for plugin object files. func (x *TestEnvController) ObjectRoot() string { return filepath.Join( - x.xdgConfigHome, pgmconfig.PgmName, plugin.PluginsDir) + x.xdgConfigHome, pgmconfig.PgmName, plugin.PluginRoot) } // SrcRoot is a objRoot directory for plugin source code @@ -120,7 +120,7 @@ func (x *TestEnvController) ObjectRoot() string { func (x *TestEnvController) SrcRoot() string { dir := filepath.Join( os.Getenv("GOPATH"), "src", - pgmconfig.Repo, pgmconfig.PgmName, plugin.PluginsDir) + pgmconfig.Repo, pgmconfig.PgmName, plugin.PluginRoot) if _, err := os.Stat(dir); err != nil { x.t.Errorf("plugin source objRoot '%s' not found", dir) } diff --git a/pkg/transformers/transformer.go b/pkg/transformers/transformer.go index fc0803fce..301fdf7bf 100644 --- a/pkg/transformers/transformer.go +++ b/pkg/transformers/transformer.go @@ -26,3 +26,8 @@ type Transformer interface { // Transform modifies data in the argument, e.g. adding labels to resources that can be labelled. Transform(m resmap.ResMap) error } + +// A Generator creates an instance of resmap.ResMap. +type Generator interface { + Generate() (resmap.ResMap, error) +} diff --git a/plugins/someteam.example.com/v1/DatePrefixer.go b/plugins/someteam.example.com/v1/DatePrefixer.go index 8f7daf93f..df389b283 100644 --- a/plugins/someteam.example.com/v1/DatePrefixer.go +++ b/plugins/someteam.example.com/v1/DatePrefixer.go @@ -13,7 +13,7 @@ import ( type plugin struct{} -var Transformer plugin +var KustomizePlugin plugin func (p *plugin) Config(k ifc.Kunstructured) error { return nil diff --git a/plugins/ServiceGenerator.go b/plugins/someteam.example.com/v1/ServiceGenerator.go similarity index 95% rename from plugins/ServiceGenerator.go rename to plugins/someteam.example.com/v1/ServiceGenerator.go index 4367daa5e..eb80a84de 100644 --- a/plugins/ServiceGenerator.go +++ b/plugins/someteam.example.com/v1/ServiceGenerator.go @@ -1,3 +1,5 @@ +// +build plugin + package main import ( @@ -15,7 +17,7 @@ type plugin struct { Port string } -var Generator plugin +var KustomizePlugin plugin var manifest = ` apiVersion: v1 diff --git a/plugins/someteam.example.com/v1/StringPrefixer.go b/plugins/someteam.example.com/v1/StringPrefixer.go index 0c8de29f9..78961e375 100644 --- a/plugins/someteam.example.com/v1/StringPrefixer.go +++ b/plugins/someteam.example.com/v1/StringPrefixer.go @@ -18,11 +18,11 @@ import ( "sigs.k8s.io/kustomize/pkg/transformers/config" ) -type plugin struct{ +type plugin struct { prefix string } -var Transformer plugin +var KustomizePlugin plugin func (p *plugin) Config(k ifc.Kunstructured) error { var err error diff --git a/plugins/someteam.example.com/v1/kvMaker.go b/plugins/someteam.example.com/v1/kvMaker.go index 2250f3e46..584beb431 100644 --- a/plugins/someteam.example.com/v1/kvMaker.go +++ b/plugins/someteam.example.com/v1/kvMaker.go @@ -1,24 +1,27 @@ // +build plugin package main + var database = map[string]string{ - "TREE": "oak", - "ROCKET": "Saturn V", - "FRUIT": "apple", - "VEGETABLE": "carrot", - "SIMPSON": "homer", + "TREE": "oak", + "ROCKET": "Saturn V", + "FRUIT": "apple", + "VEGETABLE": "carrot", + "SIMPSON": "homer", } type plugin struct{} + var KVSource plugin + func (p plugin) Get( - root string, args []string) (map[string]string, error) { - r := make(map[string]string) - for _, k := range args { - v, ok := database[k] - if ok { - r[k] = v - } - } - return r, nil + root string, args []string) (map[string]string, error) { + r := make(map[string]string) + for _, k := range args { + v, ok := database[k] + if ok { + r[k] = v + } + } + return r, nil }