mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 01:50:55 +00:00
Remove static cling in plugin development flow.
This commit is contained in:
@@ -6,6 +6,7 @@ package compiler
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -16,61 +17,74 @@ import (
|
||||
)
|
||||
|
||||
// Compiler creates Go plugin object files.
|
||||
//
|
||||
// Source code is read from
|
||||
// ${srcRoot}/${g}/${v}/${k}.go
|
||||
//
|
||||
// Object code is written to
|
||||
// ${objRoot}/${g}/${v}/${k}.so
|
||||
type Compiler struct {
|
||||
srcRoot string
|
||||
objRoot string
|
||||
// pluginRoot is where the user
|
||||
// has her ${g}/${v}/$lower(${k})/${k}.go files.
|
||||
pluginRoot string
|
||||
// Where compilation happens.
|
||||
workDir string
|
||||
// Used as the root file name for src and object.
|
||||
rawKind string
|
||||
// Capture compiler output.
|
||||
stderr bytes.Buffer
|
||||
// Capture compiler output.
|
||||
stdout bytes.Buffer
|
||||
}
|
||||
|
||||
// NewCompiler returns a new compiler instance.
|
||||
func NewCompiler(srcRoot, objRoot string) *Compiler {
|
||||
return &Compiler{srcRoot: srcRoot, objRoot: objRoot}
|
||||
func NewCompiler(root string) *Compiler {
|
||||
return &Compiler{pluginRoot: root}
|
||||
}
|
||||
|
||||
// ObjRoot is root of compilation target tree.
|
||||
func (b *Compiler) ObjRoot() string {
|
||||
return b.objRoot
|
||||
// Set GVK converts g,v,k tuples to file path components.
|
||||
func (b *Compiler) SetGVK(g, v, k string) {
|
||||
b.rawKind = k
|
||||
b.workDir = filepath.Join(b.pluginRoot, g, v, strings.ToLower(k))
|
||||
}
|
||||
|
||||
// SrcRoot is where to find src.
|
||||
func (b *Compiler) SrcRoot() string {
|
||||
return b.srcRoot
|
||||
func (b *Compiler) srcPath() string {
|
||||
return filepath.Join(b.workDir, b.rawKind+".go")
|
||||
}
|
||||
|
||||
// Compile reads ${srcRoot}/${g}/${v}/${k}.go
|
||||
// and writes ${objRoot}/${g}/${v}/${k}.so
|
||||
func (b *Compiler) Compile(g, v, k string) error {
|
||||
lowK := strings.ToLower(k)
|
||||
objDir := filepath.Join(b.objRoot, g, v, lowK)
|
||||
objFile := filepath.Join(objDir, k) + ".so"
|
||||
if FileYoungerThan(objFile, time.Minute) {
|
||||
// Skip rebuilding it.
|
||||
func (b *Compiler) objFile() string {
|
||||
return b.rawKind + ".so"
|
||||
}
|
||||
|
||||
// Absolute path to the compiler output (the .so file).
|
||||
func (b *Compiler) ObjPath() string {
|
||||
return filepath.Join(b.workDir, b.objFile())
|
||||
}
|
||||
|
||||
// Cleanup provides a hook to delete the .so file.
|
||||
// Ignore errors.
|
||||
func (b *Compiler) Cleanup() {
|
||||
_ = os.Remove(b.ObjPath())
|
||||
}
|
||||
|
||||
// Compile changes its working directory to
|
||||
// ${pluginRoot}/${g}/${v}/$lower(${k} and places
|
||||
// object code next to source code.
|
||||
func (b *Compiler) Compile() error {
|
||||
if FileYoungerThan(b.ObjPath(), 8*time.Second) {
|
||||
// Skip rebuilding it, to save time in a plugin test file
|
||||
// that has many distinct calls to make a harness and compile
|
||||
// the plugin (only the first compile will happen).
|
||||
// Make it a short time to avoid tricking someone who's actively
|
||||
// developing a plugin.
|
||||
return nil
|
||||
}
|
||||
err := os.MkdirAll(objDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcFile := filepath.Join(b.srcRoot, g, v, lowK, k) + ".go"
|
||||
if !FileExists(srcFile) {
|
||||
// Handy for tests of lone plugins.
|
||||
s := k + ".go"
|
||||
if !FileExists(s) {
|
||||
return fmt.Errorf(
|
||||
"cannot find source at '%s' or '%s'", srcFile, s)
|
||||
}
|
||||
srcFile = s
|
||||
if !FileExists(b.srcPath()) {
|
||||
return fmt.Errorf("cannot find source at '%s'", b.srcPath())
|
||||
}
|
||||
// If you use an IDE, make sure it's go build and test flags
|
||||
// match those used below. Same goes for Makefile targets.
|
||||
commands := []string{
|
||||
"build",
|
||||
// "-trimpath", This flag used to make it better, now it makes it worse,
|
||||
// see https://github.com/golang/go/issues/31354
|
||||
"-buildmode",
|
||||
"plugin",
|
||||
"-o", objFile, srcFile,
|
||||
"-o", b.objFile(),
|
||||
}
|
||||
goBin := goBin()
|
||||
if !FileExists(goBin) {
|
||||
@@ -78,12 +92,26 @@ func (b *Compiler) Compile(g, v, k string) error {
|
||||
"cannot find go compiler %s", goBin)
|
||||
}
|
||||
cmd := exec.Command(goBin, commands...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
b.stderr.Reset()
|
||||
cmd.Stderr = &b.stderr
|
||||
b.stdout.Reset()
|
||||
cmd.Stdout = &b.stdout
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Dir = b.workDir
|
||||
if err := cmd.Run(); err != nil {
|
||||
b.report()
|
||||
return errors.Wrapf(
|
||||
err, "cannot compile %s:\nSTDERR\n%s\n", srcFile, stderr.String())
|
||||
err, "cannot compile %s:\nSTDERR\n%s\n",
|
||||
b.srcPath(), b.stderr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Compiler) report() {
|
||||
log.Println("stdout: -------")
|
||||
log.Println(b.stdout.String())
|
||||
log.Println("----------------")
|
||||
log.Println("stderr: -------")
|
||||
log.Println(b.stderr.String())
|
||||
log.Println("----------------")
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
package compiler_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -16,53 +14,46 @@ import (
|
||||
|
||||
// Regression coverage over compiler behavior.
|
||||
func TestCompiler(t *testing.T) {
|
||||
configRoot, err := ioutil.TempDir("", "kustomize-compiler-test")
|
||||
if err != nil {
|
||||
t.Errorf("failed to make temp dir: %v", err)
|
||||
}
|
||||
srcRoot, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c := NewCompiler(srcRoot, configRoot)
|
||||
if configRoot != c.ObjRoot() {
|
||||
t.Errorf("unexpected objRoot %s", c.ObjRoot())
|
||||
}
|
||||
c := NewCompiler(srcRoot)
|
||||
|
||||
c.SetGVK("someteam.example.com", "v1", "DatePrefixer")
|
||||
expectObj := filepath.Join(
|
||||
c.ObjRoot(),
|
||||
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||
srcRoot, "someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
|
||||
if expectObj != c.ObjPath() {
|
||||
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
|
||||
}
|
||||
err = c.Compile("someteam.example.com", "v1", "DatePrefixer")
|
||||
err = c.Compile()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !FileYoungerThan(expectObj, time.Second) {
|
||||
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||
}
|
||||
c.Cleanup()
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file '%s' should be gone", expectObj)
|
||||
}
|
||||
|
||||
c.SetGVK("builtin", "", "SecretGenerator")
|
||||
expectObj = filepath.Join(
|
||||
c.ObjRoot(),
|
||||
srcRoot,
|
||||
"builtin", "", "secretgenerator", "SecretGenerator.so")
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||
if expectObj != c.ObjPath() {
|
||||
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
|
||||
}
|
||||
err = c.Compile("builtin", "", "SecretGenerator")
|
||||
err = c.Compile()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !FileYoungerThan(expectObj, time.Second) {
|
||||
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(c.ObjRoot())
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"removing temp dir: %s %v", c.ObjRoot(), err)
|
||||
}
|
||||
c.Cleanup()
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("cleanup failed; still see: %s", expectObj)
|
||||
t.Errorf("obj file '%s' should be gone", expectObj)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user