Change to "compile-in" plugins

Move away from the builtin 'plugin' package in favor of compile-in
plugins. This will require a plugin author to inject code into the
proper packages before building airshipctl
This commit is contained in:
Ian Howell 2019-05-17 09:23:30 -05:00
parent f76eb8e027
commit b5de5bf967
10 changed files with 47 additions and 64 deletions

View File

@ -5,12 +5,6 @@ EXECUTABLE_CLI := airshipctl
SCRIPTS_DIR := scripts SCRIPTS_DIR := scripts
PLUGIN_DIR := _plugins
PLUGIN_BIN := $(PLUGIN_DIR)/bin
PLUGIN_INT := $(patsubst $(PLUGIN_DIR)/internal/%,$(PLUGIN_BIN)/%.so,$(wildcard $(PLUGIN_DIR)/internal/*))
PLUGIN_EXT := $(wildcard $(PLUGIN_DIR)/external/*)
BINDATA := "github.com/shuLhan/go-bindata/cmd/go-bindata"
# linting # linting
LINTER_CMD := "github.com/golangci/golangci-lint/cmd/golangci-lint" run LINTER_CMD := "github.com/golangci/golangci-lint/cmd/golangci-lint" run
ADDTL_LINTERS := goconst,gofmt,lll,unparam ADDTL_LINTERS := goconst,gofmt,lll,unparam
@ -26,7 +20,7 @@ MIN_COVERAGE := 70
.PHONY: build .PHONY: build
build: plugin build:
@go build -o $(BINDIR)/$(EXECUTABLE_CLI) @go build -o $(BINDIR)/$(EXECUTABLE_CLI)
.PHONY: test .PHONY: test
@ -58,10 +52,6 @@ clean:
@rm -fr $(BINDIR) @rm -fr $(BINDIR)
@rm -fr $(COVER_FILE) @rm -fr $(COVER_FILE)
.PHONY: plugin-clean
plugin-clean:
@rm -fr $(PLUGIN_BIN)
.PHONY: docs .PHONY: docs
docs: docs:
@echo "TODO" @echo "TODO"
@ -71,11 +61,3 @@ update-golden: TESTFLAGS += -update -v
update-golden: PKG = github.com/ian-howell/airshipctl/cmd update-golden: PKG = github.com/ian-howell/airshipctl/cmd
update-golden: update-golden:
@go test $(PKG) $(TESTFLAGS) @go test $(PKG) $(TESTFLAGS)
.PHONY: plugin
plugin: $(PLUGIN_INT)
@for plugin in $(PLUGIN_EXT); do $(MAKE) -C $${plugin}; done
@go run $(BINDATA) $(PLUGIN_BIN)
$(PLUGIN_BIN)/%.so: $(PLUGIN_DIR)/*/%/*.go
@go build -buildmode=plugin -o $@ $^

View File

@ -1,2 +0,0 @@
all:
@go build -buildmode=plugin -o ../../bin/example.so example.go

View File

@ -1,4 +1,4 @@
package main package cmd
import ( import (
"fmt" "fmt"
@ -7,13 +7,10 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
//nolint:deadcode,unused,unparam func NewExampleCommand(out io.Writer, args []string) *cobra.Command {
func NewCommand(out io.Writer, args []string) *cobra.Command {
exampleCommand := &cobra.Command{ exampleCommand := &cobra.Command{
Use: "example", Use: "example",
Short: "an example command", Short: "an example command",
// Hidden is set to true because this is an example
Hidden: true,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Fprintln(out, "Hello world!") fmt.Fprintln(out, "Hello world!")
}, },

19
cmd/plugins.go Normal file
View File

@ -0,0 +1,19 @@
package cmd
import (
"io"
"github.com/spf13/cobra"
)
// builtinPlugins are the plugins that are built and maintained by the
// airshipctl team. They may be disabled if desired
var builtinPlugins = []func(io.Writer, []string) *cobra.Command{
NewWorkflowCommand,
}
// externalPlugins are external. The function to create a command should be
// placed here
var externalPlugins = []func(io.Writer, []string) *cobra.Command{
NewExampleCommand, // This is an example and shouldn't be enabled in production builds
}

View File

@ -1,27 +1,21 @@
package cmd package cmd
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"github.com/ian-howell/airshipctl/pkg/environment" "github.com/ian-howell/airshipctl/pkg/environment"
"github.com/ian-howell/airshipctl/pkg/log" "github.com/ian-howell/airshipctl/pkg/log"
"github.com/ian-howell/airshipctl/pkg/plugin"
"github.com/ian-howell/airshipctl/pkg/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var settings environment.AirshipCTLSettings var settings environment.AirshipCTLSettings
const defaultPluginDir = "_plugins/bin"
// NewRootCmd creates the root `airshipctl` command. All other commands are // NewRootCmd creates the root `airshipctl` command. All other commands are
// subcommands branching from this one // subcommands branching from this one
func NewRootCmd(out io.Writer, pluginDir string, args []string) (*cobra.Command, error) { func NewRootCmd(out io.Writer, args []string) (*cobra.Command, error) {
rootCmd := &cobra.Command{ rootCmd := &cobra.Command{
Use: "airshipctl", Use: "airshipctl",
Short: "airshipctl is a unified entrypoint to various airship components", Short: "airshipctl is a unified entrypoint to various airship components",
@ -33,21 +27,7 @@ func NewRootCmd(out io.Writer, pluginDir string, args []string) (*cobra.Command,
rootCmd.AddCommand(NewVersionCommand(out)) rootCmd.AddCommand(NewVersionCommand(out))
if _, err := os.Stat(pluginDir); err == nil { loadPluginCommands(rootCmd, out, args)
pluginFiles, err := util.ReadDir(pluginDir)
if err != nil {
return nil, errors.New("could not read plugins: " + err.Error())
}
for _, pluginFile := range pluginFiles {
pathToPlugin := filepath.Join(pluginDir, pluginFile.Name())
newCommand, err := plugin.CreateCommandFromPlugin(pathToPlugin, out, args)
if err != nil {
log.Debugf("Could not load plugin from %s: %s\n", pathToPlugin, err.Error())
} else {
rootCmd.AddCommand(newCommand)
}
}
}
log.Init(&settings, out) log.Init(&settings, out)
@ -56,14 +36,25 @@ func NewRootCmd(out io.Writer, pluginDir string, args []string) (*cobra.Command,
// Execute runs the base airshipctl command // Execute runs the base airshipctl command
func Execute(out io.Writer) { func Execute(out io.Writer) {
rootCmd, err := NewRootCmd(out, defaultPluginDir, os.Args[1:]) rootCmd, err := NewRootCmd(out, os.Args[1:])
osExitIfError(out, err)
osExitIfError(out, rootCmd.Execute())
}
func osExitIfError(out io.Writer, err error) {
if err != nil { if err != nil {
fmt.Fprintln(out, err) fmt.Fprintln(out, err)
os.Exit(1) os.Exit(1)
} }
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(out, err)
os.Exit(1)
}
}
// loadPluginCommands finds all of the plugins in the builtinPlugins and
// externalPlugins datastructures, and loads them as subcommands to cmd
func loadPluginCommands(cmd *cobra.Command, out io.Writer, args []string) {
for _, subcmd := range builtinPlugins {
cmd.AddCommand(subcmd(out, args))
}
for _, subcmd := range externalPlugins {
cmd.AddCommand(subcmd(out, args))
}
} }

View File

@ -4,8 +4,10 @@ Usage:
airshipctl [command] airshipctl [command]
Available Commands: Available Commands:
example an example command
help Help about any command help Help about any command
version Show the version number of airshipctl version Show the version number of airshipctl
workflow access to workflows
Flags: Flags:
--debug enable verbose output --debug enable verbose output

View File

@ -1,4 +1,4 @@
package main package cmd
import ( import (
"io" "io"
@ -6,11 +6,9 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
//nolint:unused
var kubeConfigFilePath string var kubeConfigFilePath string
//nolint:deadcode,unused func NewWorkflowCommand(out io.Writer, args []string) *cobra.Command {
func NewCommand(out io.Writer, args []string) *cobra.Command {
workflowRootCmd := &cobra.Command{ workflowRootCmd := &cobra.Command{
Use: "workflow", Use: "workflow",
Short: "access to workflows", Short: "access to workflows",

View File

@ -1,4 +1,4 @@
package main package cmd
import ( import (
"fmt" "fmt"
@ -13,7 +13,6 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
) )
//nolint:unused
func NewWorkflowListCommand(out io.Writer, args []string) *cobra.Command { func NewWorkflowListCommand(out io.Writer, args []string) *cobra.Command {
// TODO(howell): This is only used to appease the linter. It will be used later // TODO(howell): This is only used to appease the linter. It will be used later

View File

@ -42,7 +42,7 @@ func executeCmd(t *testing.T, command string) []byte {
var actual bytes.Buffer var actual bytes.Buffer
// TODO(howell): switch to shellwords (or similar) // TODO(howell): switch to shellwords (or similar)
args := strings.Fields(command) args := strings.Fields(command)
rootCmd, err := cmd.NewRootCmd(&actual, "testdata/_plugins/bin", args) rootCmd, err := cmd.NewRootCmd(&actual, args)
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }

View File

@ -7,8 +7,5 @@ import (
) )
func main() { func main() {
if err := RestoreAssets("", "_plugins"); err != nil {
panic(err.Error())
}
cmd.Execute(os.Stdout) cmd.Execute(os.Stdout)
} }