mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Merge pull request #1075 from monopole/pluginDogfooding
Dogfood the plugin framework.
This commit is contained in:
46
bin/pluginator.sh
Executable file
46
bin/pluginator.sh
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/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.
|
||||||
@@ -46,6 +46,10 @@ function testExamples {
|
|||||||
mdrip --mode test --label test README.md ./examples
|
mdrip --mode test --label test README.md ./examples
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateCode {
|
||||||
|
./bin/pluginator.sh $oldGoPath
|
||||||
|
}
|
||||||
|
|
||||||
# Use of GOPATH is optional if go modules are
|
# Use of GOPATH is optional if go modules are
|
||||||
# used. This script tries to work for people who
|
# used. This script tries to work for people who
|
||||||
# don't have GOPATH set, and work for travis.
|
# don't have GOPATH set, and work for travis.
|
||||||
@@ -90,6 +94,7 @@ echo pwd=`pwd`
|
|||||||
echo " "
|
echo " "
|
||||||
echo "Working..."
|
echo "Working..."
|
||||||
|
|
||||||
|
runFunc generateCode
|
||||||
runFunc testGoLangCILint
|
runFunc testGoLangCILint
|
||||||
runFunc testGoTest
|
runFunc testGoTest
|
||||||
|
|
||||||
|
|||||||
338
cmd/pluginator/main.go
Normal file
338
cmd/pluginator/main.go
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -231,16 +231,6 @@ func (kt *KustTarget) AccumulateTarget() (
|
|||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
err, "merging CRDs %v", crdTc)
|
err, "merging CRDs %v", crdTc)
|
||||||
}
|
}
|
||||||
resMap, err := kt.generateConfigMapsAndSecrets()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(
|
|
||||||
err, "generating legacy configMaps and secrets")
|
|
||||||
}
|
|
||||||
err = ra.MergeResourcesWithOverride(resMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(
|
|
||||||
err, "merging legacy configMaps and secrets")
|
|
||||||
}
|
|
||||||
err = kt.generateFromPlugins(ra)
|
err = kt.generateFromPlugins(ra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -265,14 +255,29 @@ func (kt *KustTarget) AccumulateTarget() (
|
|||||||
|
|
||||||
func (kt *KustTarget) generateFromPlugins(
|
func (kt *KustTarget) generateFromPlugins(
|
||||||
ra *accumulator.ResAccumulator) error {
|
ra *accumulator.ResAccumulator) error {
|
||||||
generators, err := kt.loadGeneratorPlugins()
|
generators, err := kt.configureBuiltinGenerators()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, g := range generators {
|
||||||
|
resMap, err := g.Generate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// The legacy generators allow override.
|
||||||
|
err = ra.MergeResourcesWithOverride(resMap)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "merging from generator %v", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generators, err = kt.loadGeneratorPlugins()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "loading generator plugins")
|
return errors.Wrap(err, "loading generator plugins")
|
||||||
}
|
}
|
||||||
for _, g := range generators {
|
for _, g := range generators {
|
||||||
resMap, err := g.Generate()
|
resMap, err := g.Generate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "generating from %v", g)
|
return err
|
||||||
}
|
}
|
||||||
err = ra.MergeResourcesWithErrorOnIdCollision(resMap)
|
err = ra.MergeResourcesWithErrorOnIdCollision(resMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -282,26 +287,6 @@ func (kt *KustTarget) generateFromPlugins(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) generateConfigMapsAndSecrets() (resmap.ResMap, error) {
|
|
||||||
cms, err := kt.rFactory.NewResMapFromConfigMapArgs(
|
|
||||||
kt.ldr,
|
|
||||||
kt.kustomization.GeneratorOptions,
|
|
||||||
kt.kustomization.ConfigMapGenerator)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(
|
|
||||||
err, "configmapgenerator: %v", kt.kustomization.ConfigMapGenerator)
|
|
||||||
}
|
|
||||||
secrets, err := kt.rFactory.NewResMapFromSecretArgs(
|
|
||||||
kt.ldr,
|
|
||||||
kt.kustomization.GeneratorOptions,
|
|
||||||
kt.kustomization.SecretGenerator)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(
|
|
||||||
err, "secretgenerator: %v", kt.kustomization.SecretGenerator)
|
|
||||||
}
|
|
||||||
return resmap.MergeWithErrorOnIdCollision(cms, secrets)
|
|
||||||
}
|
|
||||||
|
|
||||||
// accumulateResources fills the given resourceAccumulator
|
// accumulateResources fills the given resourceAccumulator
|
||||||
// with resources read from the given list of paths.
|
// with resources read from the given list of paths.
|
||||||
func (kt *KustTarget) accumulateResources(
|
func (kt *KustTarget) accumulateResources(
|
||||||
@@ -397,11 +382,11 @@ func (kt *KustTarget) newTransformer(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r = append(r, t)
|
r = append(r, t)
|
||||||
t, err = transformers.NewImageTransformer(kt.kustomization.Images, tConfig.Images)
|
lts, err := kt.configureBuiltinTransformers(tConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r = append(r, t)
|
r = append(r, lts...)
|
||||||
tp, err := kt.loadTransformerPlugins()
|
tp, err := kt.loadTransformerPlugins()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
165
pkg/target/kusttarget_configplugin.go
Normal file
165
pkg/target/kusttarget_configplugin.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
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 target
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/image"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/plugins"
|
||||||
|
"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/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Functions dedicated to configuring the builtin
|
||||||
|
// transformer and generator plugins using config data
|
||||||
|
// read from a kustomization file.
|
||||||
|
//
|
||||||
|
// Non-builtin plugins will get their configuration
|
||||||
|
// from their own dedicated structs and yaml files.
|
||||||
|
//
|
||||||
|
// There are some loops in the functions below because
|
||||||
|
// the kustomization file would, say, allow one to
|
||||||
|
// request multiple secrets be made, or run multiple
|
||||||
|
// image tag transforms, so we need to run the plugins
|
||||||
|
// N times (plugins are easier to write, configure and
|
||||||
|
// test if they do just one thing).
|
||||||
|
//
|
||||||
|
// TODO: Push code down into the plugins, as the first pass
|
||||||
|
// at this writes plugins as thin layers over calls
|
||||||
|
// into existing packages. The builtin plugins should
|
||||||
|
// be viewed as examples, and the packages they access
|
||||||
|
// directory should be public, while everything else
|
||||||
|
// should go into internal.
|
||||||
|
|
||||||
|
type generatorConfigurator func() ([]transformers.Generator, error)
|
||||||
|
type transformerConfigurator func(
|
||||||
|
tConfig *config.TransformerConfig) ([]transformers.Transformer, error)
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinGenerators() (
|
||||||
|
[]transformers.Generator, error) {
|
||||||
|
configurators := []generatorConfigurator{
|
||||||
|
kt.configureBuiltinConfigMapGenerator,
|
||||||
|
kt.configureBuiltinSecretGenerator,
|
||||||
|
}
|
||||||
|
var result []transformers.Generator
|
||||||
|
for _, f := range configurators {
|
||||||
|
r, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, r...)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinTransformers(
|
||||||
|
tConfig *config.TransformerConfig) (
|
||||||
|
[]transformers.Transformer, error) {
|
||||||
|
// TODO: Convert remaining legacy transformers to plugins
|
||||||
|
// (patch SMP/JSON, name prefix/suffix, labels/annos).
|
||||||
|
// with tests.
|
||||||
|
configurators := []transformerConfigurator{
|
||||||
|
kt.configureBuiltinImageTagTransformer,
|
||||||
|
}
|
||||||
|
var result []transformers.Transformer
|
||||||
|
for _, f := range configurators {
|
||||||
|
r, err := f(tConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, r...)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinSecretGenerator() (
|
||||||
|
result []transformers.Generator, err error) {
|
||||||
|
var c struct {
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.SecretArgs
|
||||||
|
}
|
||||||
|
if kt.kustomization.GeneratorOptions != nil {
|
||||||
|
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.SecretGenerator {
|
||||||
|
c.SecretArgs = args
|
||||||
|
p := builtingen.NewSecretGeneratorPlugin()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, "secret")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinConfigMapGenerator() (
|
||||||
|
result []transformers.Generator, err error) {
|
||||||
|
var c struct {
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.ConfigMapArgs
|
||||||
|
}
|
||||||
|
if kt.kustomization.GeneratorOptions != nil {
|
||||||
|
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.ConfigMapGenerator {
|
||||||
|
c.ConfigMapArgs = args
|
||||||
|
p := builtingen.NewConfigMapGeneratorPlugin()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, "configmap")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinImageTagTransformer(
|
||||||
|
tConfig *config.TransformerConfig) (
|
||||||
|
result []transformers.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
ImageTag image.Image
|
||||||
|
FieldSpecs []config.FieldSpec
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.Images {
|
||||||
|
c.ImageTag = args
|
||||||
|
c.FieldSpecs = tConfig.Images
|
||||||
|
p := builtingen.NewImageTagTransformerPlugin()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, "imageTag")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinPlugin(
|
||||||
|
p plugins.Configurable, c interface{}, id string) error {
|
||||||
|
y, err := yaml.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "builtin %s marshal", id)
|
||||||
|
}
|
||||||
|
err = p.Config(kt.ldr, kt.rFactory, y)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "builtin %s config: %v", id, y)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
56
plugin/builtin/ImageTagTransformer.go
Normal file
56
plugin/builtin/ImageTagTransformer.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// +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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/image"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find matching image declarations and replace
|
||||||
|
// the name, tag and/or digest.
|
||||||
|
type plugin struct {
|
||||||
|
ImageTag image.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||||
|
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var KustomizePlugin plugin
|
||||||
|
|
||||||
|
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 *plugin) Transform(m resmap.ResMap) error {
|
||||||
|
argsList := make([]image.Image, 1)
|
||||||
|
argsList[0] = p.ImageTag
|
||||||
|
t, err := transformers.NewImageTransformer(argsList, p.FieldSpecs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Transform(m)
|
||||||
|
}
|
||||||
|
|
||||||
91
plugin/builtin/ImageTagTransformer_test.go
Normal file
91
plugin/builtin/ImageTagTransformer_test.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/internal/plugintest"
|
||||||
|
"sigs.k8s.io/kustomize/k8sdeps/kv/plugin"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/kusttest"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestImageTagTransformer(t *testing.T) {
|
||||||
|
tc := plugintest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "ImageTagTransformer")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessFull(
|
||||||
|
t, "/app", loader.RestrictionRootOnly, plugin.ActivePluginConfig())
|
||||||
|
|
||||||
|
rm := th.LoadAndRunTransformer(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ImageTagTransformer
|
||||||
|
metadata:
|
||||||
|
name: notImportantHere
|
||||||
|
imageTag:
|
||||||
|
name: nginx
|
||||||
|
newTag: v2
|
||||||
|
`,`
|
||||||
|
group: apps
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deploy1
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: nginx2
|
||||||
|
image: my-nginx:1.8.0
|
||||||
|
- name: init-alpine
|
||||||
|
image: alpine:1.8.0
|
||||||
|
containers:
|
||||||
|
- name: ngnix
|
||||||
|
image: nginx:1.7.9
|
||||||
|
- name: repliaced-with-digest
|
||||||
|
image: foobar:1
|
||||||
|
- name: postgresdb
|
||||||
|
image: postgres:1.8.0
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.AssertActualEqualsExpected(rm, `
|
||||||
|
apiVersion: v1
|
||||||
|
group: apps
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deploy1
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:v2
|
||||||
|
name: ngnix
|
||||||
|
- image: foobar:1
|
||||||
|
name: repliaced-with-digest
|
||||||
|
- image: postgres:1.8.0
|
||||||
|
name: postgresdb
|
||||||
|
initContainers:
|
||||||
|
- image: my-nginx:1.8.0
|
||||||
|
name: nginx2
|
||||||
|
- image: alpine:1.8.0
|
||||||
|
name: init-alpine
|
||||||
|
`)
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//go:generate go run sigs.k8s.io/kustomize/cmd/pluginator
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
37
plugin/builtingen/ConfigMapGenerator.go
Normal file
37
plugin/builtingen/ConfigMapGenerator.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
|
||||||
|
package builtingen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigMapGeneratorPlugin struct {
|
||||||
|
ldr ifc.Loader
|
||||||
|
rf *resmap.Factory
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.ConfigMapArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigMapGeneratorPlugin() *ConfigMapGeneratorPlugin {
|
||||||
|
return &ConfigMapGeneratorPlugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConfigMapGeneratorPlugin) Config(
|
||||||
|
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
|
||||||
|
p.GeneratorOptions = types.GeneratorOptions{}
|
||||||
|
p.ConfigMapArgs = types.ConfigMapArgs{}
|
||||||
|
err = yaml.Unmarshal(config, p)
|
||||||
|
p.ldr = ldr
|
||||||
|
p.rf = rf
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||||
|
argsList := make([]types.ConfigMapArgs, 1)
|
||||||
|
argsList[0] = p.ConfigMapArgs
|
||||||
|
return p.rf.NewResMapFromConfigMapArgs(
|
||||||
|
p.ldr, &p.GeneratorOptions, argsList)
|
||||||
|
}
|
||||||
40
plugin/builtingen/ImageTagTransformer.go
Normal file
40
plugin/builtingen/ImageTagTransformer.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
|
||||||
|
package builtingen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/image"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/transformers/config"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find matching image declarations and replace
|
||||||
|
// the name, tag and/or digest.
|
||||||
|
type ImageTagTransformerPlugin struct {
|
||||||
|
ImageTag image.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||||
|
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImageTagTransformerPlugin() *ImageTagTransformerPlugin {
|
||||||
|
return &ImageTagTransformerPlugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
argsList := make([]image.Image, 1)
|
||||||
|
argsList[0] = p.ImageTag
|
||||||
|
t, err := transformers.NewImageTransformer(argsList, p.FieldSpecs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Transform(m)
|
||||||
|
}
|
||||||
|
|
||||||
37
plugin/builtingen/SecretGenerator.go
Normal file
37
plugin/builtingen/SecretGenerator.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
|
||||||
|
package builtingen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/pkg/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretGeneratorPlugin struct {
|
||||||
|
ldr ifc.Loader
|
||||||
|
rf *resmap.Factory
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.SecretArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSecretGeneratorPlugin() *SecretGeneratorPlugin {
|
||||||
|
return &SecretGeneratorPlugin{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SecretGeneratorPlugin) Config(
|
||||||
|
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
|
||||||
|
p.GeneratorOptions = types.GeneratorOptions{}
|
||||||
|
p.SecretArgs = types.SecretArgs{}
|
||||||
|
err = yaml.Unmarshal(config, p)
|
||||||
|
p.ldr = ldr
|
||||||
|
p.rf = rf
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||||
|
argsList := make([]types.SecretArgs, 1)
|
||||||
|
argsList[0] = p.SecretArgs
|
||||||
|
return p.rf.NewResMapFromSecretArgs(
|
||||||
|
p.ldr, &p.GeneratorOptions, argsList)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user