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
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
LINTER_CMD := "github.com/golangci/golangci-lint/cmd/golangci-lint" run
ADDTL_LINTERS := goconst,gofmt,lll,unparam
@ -26,7 +20,7 @@ MIN_COVERAGE := 70
.PHONY: build
build: plugin
build:
@go build -o $(BINDIR)/$(EXECUTABLE_CLI)
.PHONY: test
@ -58,10 +52,6 @@ clean:
@rm -fr $(BINDIR)
@rm -fr $(COVER_FILE)
.PHONY: plugin-clean
plugin-clean:
@rm -fr $(PLUGIN_BIN)
.PHONY: docs
docs:
@echo "TODO"
@ -71,11 +61,3 @@ update-golden: TESTFLAGS += -update -v
update-golden: PKG = github.com/ian-howell/airshipctl/cmd
update-golden:
@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 (
"fmt"
@ -7,13 +7,10 @@ import (
"github.com/spf13/cobra"
)
//nolint:deadcode,unused,unparam
func NewCommand(out io.Writer, args []string) *cobra.Command {
func NewExampleCommand(out io.Writer, args []string) *cobra.Command {
exampleCommand := &cobra.Command{
Use: "example",
Short: "an example command",
// Hidden is set to true because this is an example
Hidden: true,
Run: func(cmd *cobra.Command, args []string) {
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
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"github.com/ian-howell/airshipctl/pkg/environment"
"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"
)
var settings environment.AirshipCTLSettings
const defaultPluginDir = "_plugins/bin"
// NewRootCmd creates the root `airshipctl` command. All other commands are
// 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{
Use: "airshipctl",
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))
if _, err := os.Stat(pluginDir); err == nil {
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)
}
}
}
loadPluginCommands(rootCmd, out, args)
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
func Execute(out io.Writer) {
rootCmd, err := NewRootCmd(out, defaultPluginDir, os.Args[1:])
osExitIfError(out, err)
osExitIfError(out, rootCmd.Execute())
}
func osExitIfError(out io.Writer, err error) {
rootCmd, err := NewRootCmd(out, os.Args[1:])
if err != nil {
fmt.Fprintln(out, err)
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]
Available Commands:
example an example command
help Help about any command
version Show the version number of airshipctl
workflow access to workflows
Flags:
--debug enable verbose output

View File

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

View File

@ -1,4 +1,4 @@
package main
package cmd
import (
"fmt"
@ -13,7 +13,6 @@ import (
"k8s.io/client-go/tools/clientcmd"
)
//nolint:unused
func NewWorkflowListCommand(out io.Writer, args []string) *cobra.Command {
// 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
// TODO(howell): switch to shellwords (or similar)
args := strings.Fields(command)
rootCmd, err := cmd.NewRootCmd(&actual, "testdata/_plugins/bin", args)
rootCmd, err := cmd.NewRootCmd(&actual, args)
if err != nil {
t.Fatalf(err.Error())
}

View File

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