Separate plugins from airshipctl
This commit is contained in:
parent
df6b11515b
commit
6f879da587
18
Makefile
18
Makefile
@ -5,9 +5,10 @@ EXECUTABLE_CLI := airshipctl
|
||||
|
||||
SCRIPTS_DIR := scripts
|
||||
|
||||
PLUGIN_DIR := plugins
|
||||
PLUGIN_SOURCES := $(wildcard $(PLUGIN_DIR)/*/*.go)
|
||||
PLUGIN_OBJECTS := $(PLUGIN_SOURCES:%.go=%.so)
|
||||
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/*)
|
||||
|
||||
# linting
|
||||
LINTER_CMD := "github.com/golangci/golangci-lint/cmd/golangci-lint" run
|
||||
@ -56,6 +57,10 @@ clean:
|
||||
@rm -fr $(BINDIR)
|
||||
@rm -fr $(COVER_FILE)
|
||||
|
||||
.PHONY: plugin-clean
|
||||
plugin-clean:
|
||||
@rm -fr $(PLUGIN_BIN)
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
@echo "TODO"
|
||||
@ -67,7 +72,8 @@ update-golden:
|
||||
@go test $(PKG) $(TESTFLAGS)
|
||||
|
||||
.PHONY: plugin
|
||||
plugin: $(PLUGIN_OBJECTS)
|
||||
plugin: $(PLUGIN_INT)
|
||||
@for plugin in $(PLUGIN_EXT); do $(MAKE) -C $${plugin}; done
|
||||
|
||||
%.so: %.go
|
||||
@go build -buildmode=plugin -o $@ $<
|
||||
$(PLUGIN_BIN)/%.so: $(PLUGIN_DIR)/*/%/*.go
|
||||
@go build -buildmode=plugin -o $@ $^
|
||||
|
22
_plugins/external/example/example.go
vendored
Normal file
22
_plugins/external/example/example.go
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
//nolint:deadcode,unused,unparam
|
||||
func NewCommand(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!")
|
||||
},
|
||||
}
|
||||
return exampleCommand
|
||||
}
|
2
_plugins/external/example/makefile
vendored
Normal file
2
_plugins/external/example/makefile
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
all:
|
||||
@go build -buildmode=plugin -o ../../bin/example.so example.go
|
@ -13,23 +13,6 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
//nolint:unused
|
||||
var kubeConfigFilePath string
|
||||
|
||||
//nolint:deadcode,unused
|
||||
func NewCommand(out io.Writer, args []string) *cobra.Command {
|
||||
workflowRootCmd := &cobra.Command{
|
||||
Use: "workflow",
|
||||
Short: "access to workflows",
|
||||
Aliases: []string{"workflows", "wf"},
|
||||
}
|
||||
|
||||
workflowRootCmd.PersistentFlags().StringVar(&kubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
|
||||
workflowRootCmd.AddCommand(NewWorkflowListCommand(out, args))
|
||||
|
||||
return workflowRootCmd
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func NewWorkflowListCommand(out io.Writer, args []string) *cobra.Command {
|
||||
|
24
_plugins/internal/workflow/workflow.go
Normal file
24
_plugins/internal/workflow/workflow.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
//nolint:unused
|
||||
var kubeConfigFilePath string
|
||||
|
||||
//nolint:deadcode,unused
|
||||
func NewCommand(out io.Writer, args []string) *cobra.Command {
|
||||
workflowRootCmd := &cobra.Command{
|
||||
Use: "workflow",
|
||||
Short: "access to workflows",
|
||||
Aliases: []string{"workflows", "wf"},
|
||||
}
|
||||
|
||||
workflowRootCmd.PersistentFlags().StringVar(&kubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
|
||||
workflowRootCmd.AddCommand(NewWorkflowListCommand(out, args))
|
||||
|
||||
return workflowRootCmd
|
||||
}
|
20
cmd/root.go
20
cmd/root.go
@ -5,16 +5,20 @@ import (
|
||||
"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, args []string) (*cobra.Command, error) {
|
||||
@ -29,13 +33,15 @@ func NewRootCmd(out io.Writer, args []string) (*cobra.Command, error) {
|
||||
|
||||
rootCmd.AddCommand(NewVersionCommand(out))
|
||||
|
||||
workflowPlugin := "plugins/internal/workflow.so"
|
||||
if _, err := os.Stat(workflowPlugin); err == nil {
|
||||
rootCmd.AddCommand(plugin.CreateCommandFromPlugin(workflowPlugin, out, args))
|
||||
}
|
||||
|
||||
if err := rootCmd.PersistentFlags().Parse(args); err != nil {
|
||||
return nil, errors.New("could not parse flags: " + err.Error())
|
||||
if _, err := os.Stat(defaultPluginDir); err == nil {
|
||||
pluginFiles, err := util.ReadDir(defaultPluginDir)
|
||||
if err != nil {
|
||||
return nil, errors.New("could not read plugins: " + err.Error())
|
||||
}
|
||||
for _, pluginFile := range pluginFiles {
|
||||
pathToPlugin := filepath.Join(defaultPluginDir, pluginFile.Name())
|
||||
rootCmd.AddCommand(plugin.CreateCommandFromPlugin(pathToPlugin, out, args))
|
||||
}
|
||||
}
|
||||
|
||||
log.Init(&settings, out)
|
||||
|
1
pkg/util/test1.txt
Normal file
1
pkg/util/test1.txt
Normal file
@ -0,0 +1 @@
|
||||
testdata
|
1
pkg/util/test2.txt
Normal file
1
pkg/util/test2.txt
Normal file
@ -0,0 +1 @@
|
||||
testdata
|
1
pkg/util/test3.txt
Normal file
1
pkg/util/test3.txt
Normal file
@ -0,0 +1 @@
|
||||
testdata
|
@ -12,3 +12,13 @@ func IsReadable(path string) error {
|
||||
}
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
// ReadDir does the same thing as ioutil.ReadDir, but it doesn't sort the files.
|
||||
func ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
f, err := os.Open(dirname)
|
||||
if err != nil {
|
||||
return []os.FileInfo{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Readdir(-1)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package util_test
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/util"
|
||||
@ -38,3 +39,51 @@ func TestIsReadable(t *testing.T) {
|
||||
t.Errorf("Expected '%s' error, got '%s'", expected, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadDir(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "airshipctl-tests")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create a temporary directory: %s", err.Error())
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
testFiles := []string{
|
||||
"test1.txt",
|
||||
"test2.txt",
|
||||
"test3.txt",
|
||||
}
|
||||
|
||||
for _, testFile := range testFiles {
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, testFile), []byte("testdata"), 0666); err != nil {
|
||||
t.Fatalf("Could not create test file '%s': %s", testFile, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
files, err := util.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error while reading directory: %s", err.Error())
|
||||
}
|
||||
|
||||
if len(files) != len(testFiles) {
|
||||
t.Errorf("Expected %d files, got %d", len(testFiles), len(files))
|
||||
}
|
||||
|
||||
for _, testFile := range testFiles {
|
||||
found := false
|
||||
for _, actualFile := range files {
|
||||
if testFile == actualFile.Name() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Could not find test file '%s'", testFile)
|
||||
}
|
||||
}
|
||||
|
||||
os.RemoveAll(dir)
|
||||
|
||||
if _, err := util.ReadDir(dir); err == nil {
|
||||
t.Error("Expected an error when reading non-existant directory")
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user