One plugin per dir.

This commit is contained in:
jregan
2019-05-19 12:00:07 -07:00
parent 31534fe47d
commit 5653ae69e4
42 changed files with 612 additions and 626 deletions

View File

@@ -1,46 +0,0 @@
#!/bin/bash
#
# Converts Go-based kustomize plugins in
# sigs.k8s.io/kustomize/plugin/builtin
# (all in package 'main') to generator and
# transformer factory functions in
# sigs.k8s.io/kustomize/plugin/builtingen
# (all in package 'builtingen').
#
# Cannot put all these in the same dir, since
# plugins must be in the 'main' package,
# the factory functions cannot be in 'main',
# Go disallows multiple packages in one dir.
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
/bin/rm -rf plugin/builtingen
mkdir plugin/builtingen
GOPATH=$myGoPath go generate --tags plugin \
sigs.k8s.io/kustomize/plugin/builtin
ls -C1 plugin/builtingen
popd >& /dev/null
echo All done.

View File

@@ -47,7 +47,7 @@ function testExamples {
}
function generateCode {
./bin/pluginator.sh $oldGoPath
./plugin/generateBuiltins.sh $oldGoPath
}
# Use of GOPATH is optional if go modules are

View File

@@ -1,338 +0,0 @@
/*
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.
*/
// pluginator is a code generator that converts
// kustomize generator (G) and/or transformer (T)
// Go plugins to statically linkable code.
//
// 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 the creation
// of 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.
//
//
// HOW PLUGINS RUN
//
// Assume a file 'secGen.yaml' containing
//
// apiVersion: someteam.example.com/v1
// kind: SecretGenerator
// metadata:
// name: makesecrets
// name: mySecret
// behavior: merge
// envs:
// - db.env
// - fruit.env
//
// If this file were referenced by a kustomization
// file in its 'generators' field, kustomize would
//
// * Read 'secGen.yaml'.
//
// * Use the value of $XGD_CONFIG_HOME and
// 'apiversion' and to find an executable
// named 'SecretGenerator' to use as
// an exec plugin, or failing that,
//
// * use the same info to load a Go plugin
// object file called 'SecretGenerator.so'.
//
// * Send either the file name 'secGen.yaml' as
// the first arg to the exec plugin, or send its
// contents to the go plugin's Config method.
//
// * Use the plugin to generate and/or transform.
//
//
// GO PLUGIN CONVENTIONS
//
// A .go file can be a Go plugin if it declares
// 'main' as it's package, and exports a symbol to
// which useful functions are attached. It can
// further be used as a _kustomize_ plugin if
// those functions implement the Configurable,
// Generator and Transformer interfaces.
//
// Converting the plugin file to a normal .go package
// file is a matter string substitution permitted
// by the following conventions.
//
// * Configuration of builtin plugins:
//
// Config file looks like
//
// ---------------------------------------------
// apiVersion: builtin
// kind: SecretGenerator
// metadata:
// name: whatever
// otherFields: whatever
// ---------------------------------------------
//
// The apiVersion must be 'builtin'.
//
// For non-builtins the apiVersion can be any legal
// apiVersion value, e.g. 'someteam.example.com/v1beta1'
//
// The builtin source must be at:
//
// ${repo}/plugin/${apiVersion}/${kind}.go
//
// where repo=$GOPATH/src/sigs.k8s.io/kustomize
//
// k8s wants 'kind' values to follow CamelCase,
// while Go style wants (but doesn't demand)
// lowercase file names.
//
// kustomize will accept either idiom, but the Go file
// name must be ${kind}.go (CamelCase allowed).
//
// * Source follows this pattern
//
// ---------------------------------------------
// // +build plugin
//
// //go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
// package main
// import ...
// type plugin struct{...}
// var KustomizePlugin plugin
// func (p *plugin) Config(
// ldr ifc.Loader, rf *resmap.Factory,
// k ifc.Kunstructured) error {...}
// func (p *plugin) Generate(
// ) (resmap.ResMap, error) {...}
// func (p *plugin) Transform(
// m resmap.ResMap) error {...}
// ---------------------------------------------
//
// - The 2nd line must be empty.
// - One may `go fmt` this file.
// - There's no mention of 'SecretGenerator'
// in this file; that binding is done by
// the plugin loader or pluginator.
//
// * To compile this for loading as a Go plugin:
//
// repo=$GOPATH/src/sigs.k8s.io/kustomize
// dir=$repo/plugin/builtin
// go build -buildmode plugin -tags=plugin \
// -o $dir/SecretGenerator.so \
// $dir/SecretGenerator.go
//
// * To generate code:
//
// repo=$GOPATH/src/sigs.k8s.io/kustomize
// cd $repo/plugin/builtin
// go generate --tags plugin .
//
// This creates
//
// $repo/plugin/builtingen/SecretGenerator.go
//
// etc.
//
// * Generated plugins are used in kustomize via
//
// ---------------------------------------------
// package whatever
// import "sigs.k8s.io/kustomize/plugin/builtingen
// ...
// g := builtingen.NewSecretGenerator()
// g.Config(l, rf, k)
// resources, err := g.Generate()
// err = g.Transform(resources)
// // Eventually emit resources.
// ---------------------------------------------
//
package main
import (
"bufio"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
)
func main() {
root := fileRoot()
file, err := os.Open(root + ".go")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
processBoilerPlate(scanner, file.Name())
w := NewWriter(root)
defer w.close()
// This particular phrasing is required.
w.write(
fmt.Sprintf(
"// Code generated by pluginator on %s; DO NOT EDIT.",
root))
w.write("package builtingen")
for scanner.Scan() {
l := scanner.Text()
if strings.HasPrefix(l, "//go:generate") {
continue
}
if l == "var "+plugins.PluginSymbol+" plugin" {
w.write("func New" + root + "Plugin() *" + root + "Plugin {")
w.write(" return &" + root + "Plugin{}")
w.write("}")
continue
}
w.write(l)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
func fileRoot() string {
n := os.Getenv("GOFILE")
if !strings.HasSuffix(n, ".go") {
log.Fatalf("expecting .go suffix on %s", n)
}
return n[:len(n)-len(".go")]
}
func processBoilerPlate(s *bufio.Scanner, f string) {
if !s.Scan() {
log.Fatalf("1: %s not long enough", f)
}
first := s.Text()
if !s.Scan() {
log.Fatalf("2: %s not long enough", f)
}
next := s.Text()
if !hasPluginTag(first, next) {
log.Fatalf("%s lacks plugin tag", f)
}
gotMain := false
for !gotMain && s.Scan() {
next = s.Text()
gotMain = strings.HasPrefix(next, "package main")
}
if !gotMain {
log.Fatalf("%s missing package main", f)
}
}
func hasPluginTag(first, next string) bool {
return strings.HasPrefix(first, "// +build plugin") &&
len(next) == 0
}
type writer struct {
root string
f *os.File
}
func NewWriter(r string) *writer {
n := makeSrcFileName(r)
f, err := os.Create(n)
if err != nil {
log.Fatalf("unable to create `%s`; %v", n, err)
}
return &writer{root: r, f: f}
}
func makeSrcFileName(root string) string {
return filepath.Join(
os.Getenv("GOPATH"),
"src",
pgmconfig.DomainName,
pgmconfig.ProgramName,
pgmconfig.PluginRoot,
"builtingen",
root+".go")
}
func (w *writer) close() { w.f.Close() }
func (w *writer) write(line string) {
_, err := w.f.WriteString(w.filter(line) + "\n")
if err != nil {
log.Printf("Trouble writing: %s", line)
log.Fatal(err)
}
}
func (w *writer) filter(in string) string {
if ok, newer := w.replace(in, "type plugin struct"); ok {
return newer
}
if ok, newer := w.replace(in, "*plugin)"); ok {
return newer
}
return in
}
// replace 'plugin' with 'FooPlugin' in context
// sensitive manner.
func (w *writer) replace(in, target string) (bool, string) {
if !strings.Contains(in, target) {
return false, ""
}
newer := strings.Replace(
target, "plugin", w.root+"Plugin", 1)
return true, strings.Replace(in, target, newer, 1)
}

View File

@@ -87,8 +87,8 @@ to a sea of objects that kustomize transforms and
emits.
The specified order of transformers in the
`transformers` field is, however, respected, as
transformers aren't expected to be commutative.
`transformers` field should be respected, as
transformers cannot be expected to be commutative.
## Execution
@@ -115,7 +115,8 @@ other [k8s object]), kustomize will first look for an
_executable_ file called
```
$XDG_CONFIG_HOME/kustomize/plugin/${apiVersion}/${kind}
$XDG_CONFIG_HOME/kustomize/plugin
/${apiVersion}/LOWERCASE(${kind})/${kind}
```
The default value of `XDG_CONFIG_HOME` is `$HOME/.config`.
@@ -150,7 +151,7 @@ reminder.
### Exec plugins
[chartinflator]: ../plugin/someteam.example.com/v1/ChartInflator
[chartinflator]: ../plugin/someteam.example.com/v1/chartinflator/ChartInflator
See this example [helm chart inflator][chartInflator].
@@ -183,15 +184,13 @@ marshalled resources on `stdin` and capture
### Go plugins
[Go plugin]: https://golang.org/pkg/plugin/
[secretgenerator]: ../plugin/builtin/SecretGenerator.go
[servicegenerator]: ../plugin/someteam.example.com/v1/someservicegenerator/SomeServiceGenerator.go
See this example [secret generator][secretGenerator].
See this example [service generator][serviceGenerator].
A [Go plugin] for kustomize looks like this:
> ```
> +build plugin
>
> package main
>
> import (
@@ -205,8 +204,9 @@ A [Go plugin] for kustomize looks like this:
> var KustomizePlugin plugin
>
> func (p *plugin) Config(
> ldr ifc.Loader, rf *resmap.Factory,
> k ifc.Kunstructured) error {...}
> ldr ifc.Loader,
> rf *resmap.Factory,
> c []byte) error {...}
>
> func (p *plugin) Generate() (resmap.ResMap, error) {...}
>
@@ -218,7 +218,7 @@ The use of the identifiers `plugin`,
`Configurable`, `Generator`, `Transformer` as
shown is _required_.
The plugin author should of course change the
The plugin author will change the
contents of the `plugin` struct, and the three
method bodies, and add imports as desired.
@@ -227,10 +227,8 @@ source code is sitting right next to where the
shared object (`.so`) files are expected to be:
```
dir=$XDG_CONFIG_HOME/kustomize/plugin/${apiVersion}
go build -buildmode plugin -tags=plugin \
-o $dir/${kind}.so \
$dir/${kind}.go
d=$XDG_CONFIG_HOME/kustomize/plugin/${apiVersion}/LOWERCASE(${kind})
go build -buildmode plugin -o $d/${kind}.so $d/${kind}.go
```
#### Caveats
@@ -241,9 +239,9 @@ Go plugins allow kustomize extensions that
uses to test its _builtin_ generators and
transformers,
* run without the performance hit of firing up a
subprocess and marshalling/unmarshalling data
for each plugin run.
* run without the performance cost of firing up a
subprocess and marshalling/unmarshalling all
resource data for each plugin run.
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
@@ -263,3 +261,12 @@ a failure at load time.
Exec plugins also lack provenance, but don't
suffer from the skew problem.
In either case, at the time of writing the proper
way to share a plugin is as a tar file of source code
and associated data, developed and unpacked under
`kustomize/plugin`. In the case of a Go plugin, the
end user must compile it (described above), and may
need to compile kustomize as well. If people use
Go plugins, more tooling will be built to make
plugin sharing easier.

View File

@@ -21,6 +21,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
@@ -95,7 +96,8 @@ func goBin() string {
// Compile reads ${srcRoot}/${g}/${v}/${k}.go
// and writes ${objRoot}/${g}/${v}/${k}.so
func (b *Compiler) Compile(g, v, k string) error {
objDir := filepath.Join(b.objRoot, g, v)
lowK := strings.ToLower(k)
objDir := filepath.Join(b.objRoot, g, v, lowK)
objFile := filepath.Join(objDir, k) + ".so"
if RecentFileExists(objFile) {
// Skip rebuilding it.
@@ -105,7 +107,7 @@ func (b *Compiler) Compile(g, v, k string) error {
if err != nil {
return err
}
srcFile := filepath.Join(b.srcRoot, g, v, k) + ".go"
srcFile := filepath.Join(b.srcRoot, g, v, lowK, k) + ".go"
if !FileExists(srcFile) {
return fmt.Errorf(
"cannot find source %s", srcFile)
@@ -114,7 +116,6 @@ func (b *Compiler) Compile(g, v, k string) error {
"build",
"-buildmode",
"plugin",
"-tags=plugin",
"-o", objFile, srcFile,
}
goBin := goBin()

View File

@@ -42,7 +42,7 @@ func TestCompiler(t *testing.T) {
expectObj := filepath.Join(
c.ObjRoot(),
"someteam.example.com", "v1", "DatePrefixer.so")
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
if FileExists(expectObj) {
t.Errorf("obj file should not exist yet: %s", expectObj)
}
@@ -56,7 +56,7 @@ func TestCompiler(t *testing.T) {
expectObj = filepath.Join(
c.ObjRoot(),
"builtin", "", "SecretGenerator.so")
"builtin", "", "secretgenerator", "SecretGenerator.so")
if FileExists(expectObj) {
t.Errorf("obj file should not exist yet: %s", expectObj)
}

View File

@@ -22,7 +22,6 @@ import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
@@ -39,8 +38,8 @@ const (
// ExecPlugin record the name and args of an executable
// It triggers the executable generator and transformer
type ExecPlugin struct {
// name of the executable
name string
// absolute path of the executable
path string
// Optional command line arguments to the executable
// pulled from specially named fields in cfg.
@@ -57,15 +56,13 @@ type ExecPlugin struct {
ldr ifc.Loader
}
func NewExecPlugin(root string, id resid.ResId) *ExecPlugin {
return &ExecPlugin{
name: filepath.Join(root, pluginPath(id)),
}
func NewExecPlugin(p string) *ExecPlugin {
return &ExecPlugin{path: p}
}
// isAvailable checks to see if the plugin is available
func (p *ExecPlugin) isAvailable() bool {
f, err := os.Stat(p.name)
f, err := os.Stat(p.path)
if os.IsNotExist(err) {
return false
}
@@ -164,7 +161,7 @@ func (p *ExecPlugin) invokePlugin(input []byte) ([]byte, error) {
if err != nil {
return nil, err
}
cmd := exec.Command(p.name, args...)
cmd := exec.Command(p.path, args...)
cmd.Env = p.getEnv()
cmd.Stdin = bytes.NewReader(input)
cmd.Stderr = os.Stderr
@@ -226,7 +223,7 @@ func (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error {
idString, ok := annotations[idAnnotation]
if !ok {
return fmt.Errorf("the transformer %s should not remove annotation %s",
p.name, idAnnotation)
p.path, idAnnotation)
}
id := resid.ResId{}
err := yaml.Unmarshal([]byte(idString), &id)

View File

@@ -51,8 +51,9 @@ s/$BAR/bar/g
`))
p := NewExecPlugin(
plugin.DefaultPluginConfig().DirectoryPath,
pluginConfig.Id())
AbsolutePluginPath(
plugin.DefaultPluginConfig(),
pluginConfig.Id()))
yaml, err := pluginConfig.AsYAML()
if err != nil {
@@ -60,9 +61,9 @@ s/$BAR/bar/g
}
p.Config(ldr, rf, yaml)
expected := "/kustomize/plugin/someteam.example.com/v1/SedTransformer"
if !strings.HasSuffix(p.name, expected) {
t.Fatalf("expected suffix '%s', got '%s'", expected, p.name)
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

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"path/filepath"
"plugin"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -96,8 +97,20 @@ func (l *Loader) LoadTransformer(
return t, nil
}
func pluginPath(id resid.ResId) string {
return filepath.Join(id.Gvk().Group, id.Gvk().Version, id.Gvk().Kind)
func relativePluginPath(id resid.ResId) string {
return filepath.Join(
id.Gvk().Group,
id.Gvk().Version,
strings.ToLower(id.Gvk().Kind))
}
func AbsolutePluginPath(pc *types.PluginConfig, id resid.ResId) string {
return filepath.Join(
pc.DirectoryPath, relativePluginPath(id), id.Gvk().Kind)
}
func (l *Loader) absolutePluginPath(id resid.ResId) string {
return AbsolutePluginPath(l.pc, id)
}
func (l *Loader) loadAndConfigurePlugin(
@@ -106,7 +119,8 @@ func (l *Loader) loadAndConfigurePlugin(
return nil, errors.Errorf(
"plugins not enabled, but trying to load %s", res.Id())
}
if p := NewExecPlugin(l.pc.DirectoryPath, res.Id()); p.isAvailable() {
if p := NewExecPlugin(
l.absolutePluginPath(res.Id())); p.isAvailable() {
c = p
} else {
c, err = l.loadGoPlugin(res.Id())
@@ -135,26 +149,26 @@ func (l *Loader) loadAndConfigurePlugin(
var registry = make(map[string]Configurable)
func (l *Loader) loadGoPlugin(id resid.ResId) (c Configurable, err error) {
regId := relativePluginPath(id)
var ok bool
path := pluginPath(id)
if c, ok = registry[path]; ok {
if c, ok = registry[regId]; ok {
return c, nil
}
name := filepath.Join(l.pc.DirectoryPath, path)
p, err := plugin.Open(name + ".so")
absPath := l.absolutePluginPath(id)
p, err := plugin.Open(absPath + ".so")
if err != nil {
return nil, errors.Wrapf(err, "plugin %s fails to load", name)
return nil, errors.Wrapf(err, "plugin %s fails to load", absPath)
}
symbol, err := p.Lookup(PluginSymbol)
if err != nil {
return nil, errors.Wrapf(
err, "plugin %s doesn't have symbol %s",
name, PluginSymbol)
regId, PluginSymbol)
}
c, ok = symbol.(Configurable)
if !ok {
return nil, fmt.Errorf("plugin %s not configurable", name)
return nil, fmt.Errorf("plugin %s not configurable", regId)
}
registry[path] = c
registry[regId] = c
return
}

View File

@@ -43,7 +43,7 @@ port: "12345"
)
func TestLoader(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

View File

@@ -28,7 +28,7 @@ import (
// TODO: Download and inflate the chart, and check that
// in for the test.
func TestChartInflatorPlugin(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildExecPlugin(

View File

@@ -1,18 +1,5 @@
/*
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.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package target
@@ -23,7 +10,7 @@ import (
"sigs.k8s.io/kustomize/pkg/transformers"
"sigs.k8s.io/kustomize/pkg/transformers/config"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/plugin/builtingen"
"sigs.k8s.io/kustomize/plugin/builtin"
"sigs.k8s.io/yaml"
)
@@ -104,7 +91,7 @@ func (kt *KustTarget) configureBuiltinSecretGenerator() (
}
for _, args := range kt.kustomization.SecretGenerator {
c.SecretArgs = args
p := builtingen.NewSecretGeneratorPlugin()
p := builtin.NewSecretGeneratorPlugin()
err = kt.configureBuiltinPlugin(p, c, "secret")
if err != nil {
return nil, err
@@ -125,7 +112,7 @@ func (kt *KustTarget) configureBuiltinConfigMapGenerator() (
}
for _, args := range kt.kustomization.ConfigMapGenerator {
c.ConfigMapArgs = args
p := builtingen.NewConfigMapGeneratorPlugin()
p := builtin.NewConfigMapGeneratorPlugin()
err = kt.configureBuiltinPlugin(p, c, "configmap")
if err != nil {
return nil, err
@@ -146,7 +133,7 @@ func (kt *KustTarget) configureBuiltinNameTransformer(
c.Prefix = kt.kustomization.NamePrefix
c.Suffix = kt.kustomization.NameSuffix
c.FieldSpecs = tConfig.NamePrefix
p := builtingen.NewNameTransformerPlugin()
p := builtin.NewNameTransformerPlugin()
err = kt.configureBuiltinPlugin(p, c, "name")
if err != nil {
return nil, err
@@ -165,7 +152,7 @@ func (kt *KustTarget) configureBuiltinImageTagTransformer(
for _, args := range kt.kustomization.Images {
c.ImageTag = args
c.FieldSpecs = tConfig.Images
p := builtingen.NewImageTagTransformerPlugin()
p := builtin.NewImageTagTransformerPlugin()
err = kt.configureBuiltinPlugin(p, c, "imageTag")
if err != nil {
return nil, err

View File

@@ -24,7 +24,7 @@ import (
)
func TestPluginDir(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildExecPlugin(

View File

@@ -47,7 +47,7 @@ metadata:
}
func TestOrderedTransformers(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(
@@ -92,7 +92,7 @@ spec:
}
func TestSedTransformer(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildExecPlugin(
@@ -161,7 +161,7 @@ metadata:
}
func TestTransformedTransformers(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

View File

@@ -1,10 +1,5 @@
// +build plugin
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
package main
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
package builtin
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -13,16 +8,18 @@ import (
"sigs.k8s.io/yaml"
)
type plugin struct {
type ConfigMapGeneratorPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
types.GeneratorOptions
types.ConfigMapArgs
}
var KustomizePlugin plugin
func NewConfigMapGeneratorPlugin() *ConfigMapGeneratorPlugin {
return &ConfigMapGeneratorPlugin{}
}
func (p *plugin) Config(
func (p *ConfigMapGeneratorPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.ConfigMapArgs = types.ConfigMapArgs{}
@@ -32,7 +29,7 @@ func (p *plugin) Config(
return
}
func (p *plugin) Generate() (resmap.ResMap, error) {
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
argsList := make([]types.ConfigMapArgs, 1)
argsList[0] = p.ConfigMapArgs
return p.rf.NewResMapFromConfigMapArgs(

View File

@@ -1,10 +1,5 @@
// +build plugin
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
package main
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
package builtin
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -17,21 +12,23 @@ import (
// Find matching image declarations and replace
// the name, tag and/or digest.
type plugin struct {
type ImageTagTransformerPlugin struct {
ImageTag image.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
var KustomizePlugin plugin
func NewImageTagTransformerPlugin() *ImageTagTransformerPlugin {
return &ImageTagTransformerPlugin{}
}
func (p *plugin) Config(
func (p *ImageTagTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ImageTag = image.Image{}
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *plugin) Transform(m resmap.ResMap) error {
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
argsList := make([]image.Image, 1)
argsList[0] = p.ImageTag
t, err := transformers.NewImageTransformer(argsList, p.FieldSpecs)

View File

@@ -1,10 +1,5 @@
// +build plugin
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
package main
// Code generated by pluginator on NameTransformer; DO NOT EDIT.
package builtin
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -15,15 +10,17 @@ import (
)
// Add the given prefix and suffix to the resource name.
type plugin struct {
type NameTransformerPlugin struct {
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
var KustomizePlugin plugin
func NewNameTransformerPlugin() *NameTransformerPlugin {
return &NameTransformerPlugin{}
}
func (p *plugin) Config(
func (p *NameTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.Prefix = ""
p.Suffix = ""
@@ -31,7 +28,7 @@ func (p *plugin) Config(
return yaml.Unmarshal(c, p)
}
func (p *plugin) Transform(m resmap.ResMap) error {
func (p *NameTransformerPlugin) Transform(m resmap.ResMap) error {
t, err := transformers.NewNamePrefixSuffixTransformer(
p.Prefix,
p.Suffix,

View File

@@ -1,10 +1,5 @@
// +build plugin
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
package main
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
package builtin
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -13,16 +8,18 @@ import (
"sigs.k8s.io/yaml"
)
type plugin struct {
type SecretGeneratorPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
types.GeneratorOptions
types.SecretArgs
}
var KustomizePlugin plugin
func NewSecretGeneratorPlugin() *SecretGeneratorPlugin {
return &SecretGeneratorPlugin{}
}
func (p *plugin) Config(
func (p *SecretGeneratorPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.SecretArgs = types.SecretArgs{}
@@ -32,7 +29,7 @@ func (p *plugin) Config(
return
}
func (p *plugin) Generate() (resmap.ResMap, error) {
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
argsList := make([]types.SecretArgs, 1)
argsList[0] = p.SecretArgs
return p.rf.NewResMapFromSecretArgs(

View File

@@ -1,5 +1,8 @@
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
package builtingen
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator
package main
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -8,18 +11,16 @@ import (
"sigs.k8s.io/yaml"
)
type ConfigMapGeneratorPlugin struct {
type plugin struct {
ldr ifc.Loader
rf *resmap.Factory
types.GeneratorOptions
types.ConfigMapArgs
}
func NewConfigMapGeneratorPlugin() *ConfigMapGeneratorPlugin {
return &ConfigMapGeneratorPlugin{}
}
var KustomizePlugin plugin
func (p *ConfigMapGeneratorPlugin) Config(
func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.ConfigMapArgs = types.ConfigMapArgs{}
@@ -29,7 +30,7 @@ func (p *ConfigMapGeneratorPlugin) Config(
return
}
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
func (p *plugin) Generate() (resmap.ResMap, error) {
argsList := make([]types.ConfigMapArgs, 1)
argsList[0] = p.ConfigMapArgs
return p.rf.NewResMapFromConfigMapArgs(

View File

@@ -11,7 +11,7 @@ import (
)
func TestConfigMapGenerator(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

View File

@@ -1,5 +1,8 @@
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
package builtingen
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator
package main
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -12,23 +15,21 @@ import (
// Find matching image declarations and replace
// the name, tag and/or digest.
type ImageTagTransformerPlugin struct {
type plugin struct {
ImageTag image.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func NewImageTagTransformerPlugin() *ImageTagTransformerPlugin {
return &ImageTagTransformerPlugin{}
}
var KustomizePlugin plugin
func (p *ImageTagTransformerPlugin) Config(
func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ImageTag = image.Image{}
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
func (p *plugin) Transform(m resmap.ResMap) error {
argsList := make([]image.Image, 1)
argsList[0] = p.ImageTag
t, err := transformers.NewImageTransformer(argsList, p.FieldSpecs)

View File

@@ -11,7 +11,7 @@ import (
)
func TestImageTagTransformer(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

View File

@@ -1,5 +1,8 @@
// Code generated by pluginator on NameTransformer; DO NOT EDIT.
package builtingen
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator
package main
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -10,17 +13,15 @@ import (
)
// Add the given prefix and suffix to the resource name.
type NameTransformerPlugin struct {
type plugin struct {
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func NewNameTransformerPlugin() *NameTransformerPlugin {
return &NameTransformerPlugin{}
}
var KustomizePlugin plugin
func (p *NameTransformerPlugin) Config(
func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.Prefix = ""
p.Suffix = ""
@@ -28,7 +29,7 @@ func (p *NameTransformerPlugin) Config(
return yaml.Unmarshal(c, p)
}
func (p *NameTransformerPlugin) Transform(m resmap.ResMap) error {
func (p *plugin) Transform(m resmap.ResMap) error {
t, err := transformers.NewNamePrefixSuffixTransformer(
p.Prefix,
p.Suffix,

View File

@@ -11,7 +11,7 @@ import (
)
func TestNameTransformer(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

View File

@@ -1,5 +1,8 @@
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
package builtingen
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator
package main
import (
"sigs.k8s.io/kustomize/pkg/ifc"
@@ -8,18 +11,16 @@ import (
"sigs.k8s.io/yaml"
)
type SecretGeneratorPlugin struct {
type plugin struct {
ldr ifc.Loader
rf *resmap.Factory
types.GeneratorOptions
types.SecretArgs
}
func NewSecretGeneratorPlugin() *SecretGeneratorPlugin {
return &SecretGeneratorPlugin{}
}
var KustomizePlugin plugin
func (p *SecretGeneratorPlugin) Config(
func (p *plugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.SecretArgs = types.SecretArgs{}
@@ -29,7 +30,7 @@ func (p *SecretGeneratorPlugin) Config(
return
}
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
func (p *plugin) Generate() (resmap.ResMap, error) {
argsList := make([]types.SecretArgs, 1)
argsList[0] = p.SecretArgs
return p.rf.NewResMapFromSecretArgs(

View File

@@ -11,7 +11,7 @@ import (
)
func TestSecretGenerator(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

237
plugin/doc.go Normal file
View File

@@ -0,0 +1,237 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
/*
Package plugin contains builtin and example
plugins, tests and test libraries, and a code
generator for converting a plugin to statically
loadable code (see pluginator).
HOW PLUGINS RUN
Assume a file 'secGen.yaml' containing
apiVersion: someteam.example.com/v1
kind: SecretGenerator
metadata:
name: makesecrets
name: mySecret
behavior: merge
envs:
- db.env
- fruit.env
If this file were referenced by a kustomization
file in its 'generators' field, kustomize would
* Read 'secGen.yaml'.
* Use the value of $XGD_CONFIG_HOME and
'apiversion' and to find an executable
named 'SecretGenerator' to use as
an exec plugin, or failing that,
* use the same info to load a Go plugin
object file called 'SecretGenerator.so'.
* Send either the file name 'secGen.yaml' as
the first arg to the exec plugin, or send its
contents to the go plugin's Config method.
* Use the plugin to generate and/or transform.
GO PLUGINS
A .go file can be a Go plugin if it declares
'main' as it's package, and exports a symbol to
which useful functions are attached.
It can further be used as a _kustomize_ plugin if
the symbol is named 'KustomizePlugin' and the
attached functions implement the Configurable,
Generator and Transformer interfaces.
A plugin won't load into some program foo/main.go
if there is any package version mismatch in the
dependencies of the plugin and the dependencies of
foo/main.go. Control this with matching
declarations in go.mod files. The versions of the
builtin packages "fmt", "io", "os" (not normally
listed in go.mod) etc have the same version as the
compiler.
ONE PLUGIN PER DIRECTORY
For kustomize (and perhaps anyone), it's simplest
to put each plugin into its own directory.
Go plugins must be in package `main`, and so
having more than one plugin in a directory means
their loading symbols have to differ, which makes
it hard to standardize around how they get loaded,
or it means one must use build tags to suppress
full directory compilation - which creates
difficulties using IDEs, the `go mod` tool, `go
test ./...`, etc.
A one plugin per directory policy makes it easy to
define the plugin as a module, with its own
`go.mod` file - which is vital for resolving
package version dependency mismatches at load
time. It also makes it easy to create a plugin
tarball (source, test, go.mod, plugin data files,
etc.) for distribution.
BUILTIN PLUGIN CONFIGURATION
For performance reasons, all builting 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.
For non-builtins the apiVersion can be any legal
apiVersion value, e.g. 'someteam.example.com/v1beta1'
The builtin source must be at:
repo=$GOPATH/src/sigs.k8s.io/kustomize
${repo}/plugin/${apiVersion}/LOWERCASE(${kind})/${kind}.go
(dropping the ".go" for exec plugins).
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).
PLUGIN SOURCE
* Pattern
secretgenerator.go
---------------------------------------------
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
package main
import ...
type plugin struct{...}
var KustomizePlugin plugin
func (p *plugin) Config(
ldr ifc.Loader,
rf *resmap.Factory,
c []byte) error {...}
func (p *plugin) Generate(
) (resmap.ResMap, error) {...}
func (p *plugin) Transform(
m resmap.ResMap) error {...}
---------------------------------------------
The plugin name doesn't appear in the file itself.
* Compilation
repo=$GOPATH/src/sigs.k8s.io/kustomize
dir=$repo/plugin/builtin
go build -buildmode plugin \
-o $dir/secretgenerator.so \
$dir/secretgenerator.go
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 the creation
of 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/plugin/builtin
go generate ./...
This creates
$repo/plugin/builtin/SecretGenerator.go
etc.
Generated plugins are used in kustomize via
---------------------------------------------
package whatever
import "sigs.k8s.io/kustomize/plugin/builtin
...
g := builtin.NewSecretGenerator()
g.Config(l, rf, k)
resources, err := g.Generate()
err = g.Transform(resources)
// Eventually emit resources.
---------------------------------------------
*/
package plugin

View File

@@ -8,15 +8,16 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
)
// PluginTestEnv manages the plugin test environment.
// EnvForTest manages the plugin test environment.
// It sets/resets XDG_CONFIG_HOME, makes/removes a temp objRoot.
type PluginTestEnv struct {
type EnvForTest struct {
t *testing.T
compiler *plugins.Compiler
workDir string
@@ -24,34 +25,33 @@ type PluginTestEnv struct {
wasSet bool
}
func NewPluginTestEnv(t *testing.T) *PluginTestEnv {
return &PluginTestEnv{t: t}
func NewEnvForTest(t *testing.T) *EnvForTest {
return &EnvForTest{t: t}
}
func (x *PluginTestEnv) Set() *PluginTestEnv {
func (x *EnvForTest) Set() *EnvForTest {
x.createWorkDir()
x.compiler = x.makeCompiler()
x.setEnv()
return x
}
func (x *PluginTestEnv) Reset() {
func (x *EnvForTest) Reset() {
x.resetEnv()
x.removeWorkDir()
}
func (x *PluginTestEnv) BuildGoPlugin(g, v, k string) {
func (x *EnvForTest) BuildGoPlugin(g, v, k string) {
err := x.compiler.Compile(g, v, k)
if err != nil {
x.t.Errorf("compile failed: %v", err)
}
}
func (x *PluginTestEnv) BuildExecPlugin(name ...string) {
obj := filepath.Join(
append([]string{x.compiler.ObjRoot()}, name...)...)
src := filepath.Join(
append([]string{x.compiler.SrcRoot()}, name...)...)
func (x *EnvForTest) BuildExecPlugin(g, v, k string) {
lowK := strings.ToLower(k)
obj := filepath.Join(x.compiler.ObjRoot(), g, v, lowK, k)
src := filepath.Join(x.compiler.SrcRoot(), g, v, lowK, k)
if err := os.MkdirAll(filepath.Dir(obj), 0755); err != nil {
x.t.Errorf("error making directory: %s", filepath.Dir(obj))
}
@@ -62,7 +62,7 @@ func (x *PluginTestEnv) BuildExecPlugin(name ...string) {
}
}
func (x *PluginTestEnv) makeCompiler() *plugins.Compiler {
func (x *EnvForTest) makeCompiler() *plugins.Compiler {
// The plugin loader wants to find object code under
// $XDG_CONFIG_HOME/kustomize/plugins
// and the compiler writes object code to
@@ -81,7 +81,7 @@ func (x *PluginTestEnv) makeCompiler() *plugins.Compiler {
return plugins.NewCompiler(srcRoot, objRoot)
}
func (x *PluginTestEnv) createWorkDir() {
func (x *EnvForTest) createWorkDir() {
var err error
x.workDir, err = ioutil.TempDir("", "kustomize-plugin-tests")
if err != nil {
@@ -89,7 +89,7 @@ func (x *PluginTestEnv) createWorkDir() {
}
}
func (x *PluginTestEnv) removeWorkDir() {
func (x *EnvForTest) removeWorkDir() {
err := os.RemoveAll(x.workDir)
if err != nil {
x.t.Errorf(
@@ -97,12 +97,12 @@ func (x *PluginTestEnv) removeWorkDir() {
}
}
func (x *PluginTestEnv) setEnv() {
func (x *EnvForTest) setEnv() {
x.oldXdg, x.wasSet = os.LookupEnv(pgmconfig.XDG_CONFIG_HOME)
os.Setenv(pgmconfig.XDG_CONFIG_HOME, x.workDir)
}
func (x *PluginTestEnv) resetEnv() {
func (x *EnvForTest) resetEnv() {
if x.wasSet {
os.Setenv(pgmconfig.XDG_CONFIG_HOME, x.oldXdg)
} else {

37
plugin/generateBuiltins.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/bin/bash
#
# This script creates the generator and
# transformer factory functions in
# sigs.k8s.io/kustomize/plugin/builtin
# by generating code based on the plugins
# 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/plugin/builtin/...
popd >& /dev/null
echo All done.

132
plugin/pluginator/main.go Normal file
View File

@@ -0,0 +1,132 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// See plugin/doc.go for an explanation.
package main
import (
"bufio"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
)
func main() {
root := inputFileRoot()
file, err := os.Open(root + ".go")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
readToPackageMain(scanner, file.Name())
w := NewWriter(root)
defer w.close()
// This particular phrasing is required.
w.write(
fmt.Sprintf(
"// Code generated by pluginator on %s; DO NOT EDIT.",
root))
w.write("package builtin")
for scanner.Scan() {
l := scanner.Text()
if strings.HasPrefix(l, "//go:generate") {
continue
}
if l == "var "+plugins.PluginSymbol+" plugin" {
w.write("func New" + root + "Plugin() *" + root + "Plugin {")
w.write(" return &" + root + "Plugin{}")
w.write("}")
continue
}
w.write(l)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
func inputFileRoot() string {
n := os.Getenv("GOFILE")
if !strings.HasSuffix(n, ".go") {
log.Fatalf("expecting .go suffix on %s", n)
}
return n[:len(n)-len(".go")]
}
func readToPackageMain(s *bufio.Scanner, f string) {
gotMain := false
for !gotMain && s.Scan() {
gotMain = strings.HasPrefix(s.Text(), "package main")
}
if !gotMain {
log.Fatalf("%s missing package main", f)
}
}
type writer struct {
root string
f *os.File
}
func NewWriter(r string) *writer {
n := makeOutputFileName(r)
f, err := os.Create(n)
if err != nil {
log.Fatalf("unable to create `%s`; %v", n, err)
}
return &writer{root: r, f: f}
}
func makeOutputFileName(root string) string {
return filepath.Join(
os.Getenv("GOPATH"),
"src",
pgmconfig.DomainName,
pgmconfig.ProgramName,
pgmconfig.PluginRoot,
"builtin",
root+".go")
}
func (w *writer) close() {
fmt.Println("Generated " + w.root)
w.f.Close()
}
func (w *writer) write(line string) {
_, err := w.f.WriteString(w.filter(line) + "\n")
if err != nil {
log.Printf("Trouble writing: %s", line)
log.Fatal(err)
}
}
func (w *writer) filter(in string) string {
if ok, newer := w.replace(in, "type plugin struct"); ok {
return newer
}
if ok, newer := w.replace(in, "*plugin)"); ok {
return newer
}
return in
}
// replace 'plugin' with 'FooPlugin' in context
// sensitive manner.
func (w *writer) replace(in, target string) (bool, string) {
if !strings.Contains(in, target) {
return false, ""
}
newer := strings.Replace(
target, "plugin", w.root+"Plugin", 1)
return true, strings.Replace(in, target, newer, 1)
}

View File

@@ -1,8 +1,9 @@
#!/bin/bash
set -e
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
# Helm chart inflator
#
# Reads a file like this
#
# apiVersion: kustomize.config.k8s.io/v1
@@ -23,6 +24,7 @@ set -e
# Example execution:
# ./plugin/someteam.example.com/v1/ChartInflator configFile.yaml
set -e
# Yaml parsing is a ridiculous thing to do in bash,
# but let's try:

View File

@@ -19,7 +19,7 @@ import (
// TODO: Download and inflate the chart, and check that
// in for the test.
func TestChartInflator(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildExecPlugin(

View File

@@ -1,20 +1,5 @@
// +build plugin
/*
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.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main

View File

@@ -1,43 +0,0 @@
// +build plugin
/*
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 main
var database = map[string]string{
"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
}

View File

@@ -0,0 +1,25 @@
package kvmaker
var database = map[string]string{
"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
}

View File

@@ -11,7 +11,7 @@ import (
)
func TestSedTransformer(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildExecPlugin("someteam.example.com", "v1", "SedTransformer")

View File

@@ -1,5 +1,3 @@
// +build plugin
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

View File

@@ -11,7 +11,7 @@ import (
)
func TestSomeServiceGeneratorPlugin(t *testing.T) {
tc := plugin.NewPluginTestEnv(t).Set()
tc := plugin.NewEnvForTest(t).Set()
defer tc.Reset()
tc.BuildGoPlugin(

View File

@@ -1,5 +1,3 @@
// +build plugin
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0