Overhaul for plugin system
This commit is contained in:
parent
49ddd449a7
commit
33fecc360b
@ -1,19 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/ian-howell/airshipctl/cmd/workflow"
|
||||
|
||||
// "github.com/ian-howell/exampleplugin"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
)
|
||||
|
||||
// pluginCommands are the functions that create the entrypoint command for a plugin
|
||||
var pluginCommands = []func(io.Writer, *environment.AirshipCTLSettings, []string) *cobra.Command{
|
||||
// exampleplugin.NewExampleCommand, // This is an example and shouldn't be enabled in production builds
|
||||
workflow.NewWorkflowCommand,
|
||||
}
|
38
cmd/root.go
38
cmd/root.go
@ -1,55 +1,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
"github.com/ian-howell/airshipctl/pkg/log"
|
||||
)
|
||||
|
||||
// NewRootCmd creates the root `airshipctl` command. All other commands are
|
||||
// subcommands branching from this one
|
||||
func NewRootCmd(out io.Writer, settings *environment.AirshipCTLSettings, args []string) (*cobra.Command, error) {
|
||||
func NewRootCmd(out io.Writer) (*cobra.Command, error) {
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "airshipctl",
|
||||
Short: "airshipctl is a unified entrypoint to various airship components",
|
||||
}
|
||||
rootCmd.SetOutput(out)
|
||||
|
||||
settings.InitFlags(rootCmd)
|
||||
|
||||
rootCmd.AddCommand(NewVersionCommand(out))
|
||||
|
||||
loadPluginCommands(rootCmd, out, settings, args)
|
||||
|
||||
rootCmd.PersistentFlags().Parse(args)
|
||||
|
||||
settings.InitDefaults()
|
||||
log.Init(settings, out)
|
||||
|
||||
return rootCmd, nil
|
||||
}
|
||||
|
||||
// Execute runs the base airshipctl command
|
||||
func Execute(out io.Writer) {
|
||||
rootCmd, err := NewRootCmd(out, &environment.AirshipCTLSettings{}, 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 loads all of the plugins as subcommands to cmd
|
||||
func loadPluginCommands(cmd *cobra.Command, out io.Writer, settings *environment.AirshipCTLSettings, args []string) {
|
||||
for _, subcmd := range pluginCommands {
|
||||
cmd.AddCommand(subcmd(out, settings, args))
|
||||
}
|
||||
}
|
||||
|
@ -6,18 +6,25 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
||||
)
|
||||
|
||||
// PluginSettingsID is used as a key in the root settings map of plugin settings
|
||||
const PluginSettingsID = "argo"
|
||||
|
||||
// NewWorkflowCommand creates a new command for working with argo workflows
|
||||
func NewWorkflowCommand(out io.Writer, settings *environment.AirshipCTLSettings, args []string) *cobra.Command {
|
||||
func NewWorkflowCommand(out io.Writer, rootSettings *environment.AirshipCTLSettings) *cobra.Command {
|
||||
workflowRootCmd := &cobra.Command{
|
||||
Use: "workflow",
|
||||
Short: "Access to argo workflows",
|
||||
Aliases: []string{"workflows", "wf"},
|
||||
}
|
||||
|
||||
workflowRootCmd.AddCommand(NewWorkflowInitCommand(out, settings, args))
|
||||
workflowRootCmd.AddCommand(NewWorkflowListCommand(out, settings, args))
|
||||
wfSettings := &wfenv.Settings{}
|
||||
wfSettings.InitFlags(workflowRootCmd)
|
||||
workflowRootCmd.AddCommand(NewWorkflowInitCommand(out, wfSettings))
|
||||
workflowRootCmd.AddCommand(NewWorkflowListCommand(out, rootSettings))
|
||||
rootSettings.Register(PluginSettingsID, wfSettings)
|
||||
|
||||
return workflowRootCmd
|
||||
}
|
||||
|
@ -13,9 +13,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -28,16 +27,16 @@ var (
|
||||
|
||||
type workflowInitCmd struct {
|
||||
out io.Writer
|
||||
config *rest.Config
|
||||
kubeclient kubernetes.Interface
|
||||
crdclient apixv1beta1client.ApiextensionsV1beta1Interface
|
||||
}
|
||||
|
||||
// NewWorkflowInitCommand is a command for bootstrapping a kubernetes cluster with the necessary components for Argo workflows
|
||||
func NewWorkflowInitCommand(out io.Writer, settings *environment.AirshipCTLSettings, args []string) *cobra.Command {
|
||||
func NewWorkflowInitCommand(out io.Writer, settings *environment.Settings) *cobra.Command {
|
||||
workflowInit := &workflowInitCmd{
|
||||
out: out,
|
||||
config: settings.KubeConfig,
|
||||
kubeclient: settings.KubeClient,
|
||||
crdclient: settings.CRDClient,
|
||||
}
|
||||
workflowInitCommand := &cobra.Command{
|
||||
Use: "init [flags]",
|
||||
@ -160,11 +159,7 @@ func (wfInit *workflowInitCmd) createCustomObjects(manifestPath string) {
|
||||
}
|
||||
|
||||
func (wfInit *workflowInitCmd) registerDefaultWorkflow() error {
|
||||
apixClient, err := apixv1beta1client.NewForConfig(wfInit.config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not create CRD client: %s", err.Error())
|
||||
}
|
||||
|
||||
apixClient := wfInit.crdclient
|
||||
crds := apixClient.CustomResourceDefinitions()
|
||||
workflowCRD := &apixv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -185,7 +180,7 @@ func (wfInit *workflowInitCmd) registerDefaultWorkflow() error {
|
||||
Scope: apixv1beta1.NamespaceScoped,
|
||||
},
|
||||
}
|
||||
_, err = crds.Create(workflowCRD)
|
||||
_, err := crds.Create(workflowCRD)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -5,26 +5,27 @@ import (
|
||||
"io"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/argo/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
|
||||
)
|
||||
|
||||
// NewWorkflowListCommand is a command for listing argo workflows
|
||||
func NewWorkflowListCommand(out io.Writer, settings *environment.AirshipCTLSettings, args []string) *cobra.Command {
|
||||
func NewWorkflowListCommand(out io.Writer, rootSettings *environment.AirshipCTLSettings) *cobra.Command {
|
||||
workflowListCmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list workflows",
|
||||
Aliases: []string{"ls"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clientSet, err := v1alpha1.NewForConfig(settings.KubeConfig)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
wfSettings, ok := rootSettings.PluginSettings[PluginSettingsID].(*wfenv.Settings)
|
||||
if !ok {
|
||||
fmt.Fprintf(out, "settings for %s were not registered\n", PluginSettingsID)
|
||||
return
|
||||
}
|
||||
|
||||
wflist, err := clientSet.Workflows(settings.Namespace).List(v1.ListOptions{})
|
||||
clientSet := wfSettings.ArgoClient
|
||||
wflist, err := clientSet.Workflows(wfSettings.Namespace).List(v1.ListOptions{})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
1
go.sum
1
go.sum
@ -70,6 +70,7 @@ github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+
|
||||
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
@ -6,12 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ian-howell/airshipctl/cmd"
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
// UpdateGolden writes out the golden files with the latest values, rather than failing the test.
|
||||
@ -26,41 +21,7 @@ const (
|
||||
// CmdTest is a command to be run on the command line as a test
|
||||
type CmdTest struct {
|
||||
Name string
|
||||
Command string
|
||||
}
|
||||
|
||||
// RunCmdTests checks all of the tests actual output against their expected outputs
|
||||
func RunCmdTests(t *testing.T, tests []CmdTest) {
|
||||
t.Helper()
|
||||
for _, test := range tests {
|
||||
cmdOutput := executeCmd(t, test.Command)
|
||||
if *shouldUpdateGolden {
|
||||
updateGolden(t, test, cmdOutput)
|
||||
} else {
|
||||
assertEqualGolden(t, test, cmdOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func executeCmd(t *testing.T, command string) []byte {
|
||||
var actual bytes.Buffer
|
||||
settings := &environment.AirshipCTLSettings{
|
||||
KubeClient: fake.NewSimpleClientset(),
|
||||
}
|
||||
// TODO(howell): switch to shellwords (or similar)
|
||||
args := strings.Fields(command)
|
||||
rootCmd, err := cmd.NewRootCmd(&actual, settings, args)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
rootCmd.SetArgs(args)
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
return actual.Bytes()
|
||||
CmdLine string
|
||||
}
|
||||
|
||||
func updateGolden(t *testing.T, test CmdTest, actual []byte) {
|
||||
|
27
main.go
27
main.go
@ -1,11 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ian-howell/airshipctl/cmd"
|
||||
"github.com/ian-howell/airshipctl/cmd/workflow"
|
||||
|
||||
"github.com/ian-howell/airshipctl/pkg/environment"
|
||||
"github.com/ian-howell/airshipctl/pkg/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute(os.Stdout)
|
||||
rootCmd, err := cmd.NewRootCmd(os.Stdout)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stdout, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
settings := &environment.AirshipCTLSettings{}
|
||||
settings.InitFlags(rootCmd)
|
||||
|
||||
rootCmd.AddCommand(workflow.NewWorkflowCommand(os.Stdout, settings))
|
||||
|
||||
rootCmd.PersistentFlags().Parse(os.Args[1:])
|
||||
|
||||
settings.Init()
|
||||
|
||||
log.Init(settings, os.Stdout)
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stdout, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -2,57 +2,36 @@ package environment
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type PluginSettings interface {
|
||||
Init() error
|
||||
}
|
||||
|
||||
// AirshipCTLSettings is a container for all of the settings needed by airshipctl
|
||||
type AirshipCTLSettings struct {
|
||||
// KubeConfigFilePath is the path to the kubernetes configuration file.
|
||||
// This flag is only needed when airshipctl is being used
|
||||
// out-of-cluster
|
||||
KubeConfigFilePath string
|
||||
|
||||
// KubeConfig contains the configuration details needed for interacting
|
||||
// with the cluster
|
||||
KubeConfig *restclient.Config
|
||||
|
||||
// KubeClient contains a kubernetes clientset
|
||||
KubeClient kubernetes.Interface
|
||||
|
||||
// Namespace is the kubernetes namespace to be used during the context of this action
|
||||
Namespace string
|
||||
|
||||
// Debug is used for verbose output
|
||||
Debug bool
|
||||
|
||||
PluginSettings map[string]PluginSettings
|
||||
}
|
||||
|
||||
// InitFlags adds the default settings flags to cmd
|
||||
func (a *AirshipCTLSettings) InitFlags(cmd *cobra.Command) {
|
||||
flags := cmd.PersistentFlags()
|
||||
flags.BoolVar(&a.Debug, "debug", false, "enable verbose output")
|
||||
flags.StringVar(&a.KubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
|
||||
flags.StringVar(&a.Namespace, "namespace", "default", "kubernetes namespace to use for the context of this command")
|
||||
}
|
||||
|
||||
// InitDefaults assigns default values for any value that has not been previously set
|
||||
func (a *AirshipCTLSettings) InitDefaults() error {
|
||||
if a.KubeConfigFilePath == "" {
|
||||
a.KubeConfigFilePath = clientcmd.RecommendedHomeFile
|
||||
func (a *AirshipCTLSettings) Register(pluginName string, pluginSettings PluginSettings) {
|
||||
if a.PluginSettings == nil {
|
||||
a.PluginSettings = make(map[string]PluginSettings, 0)
|
||||
}
|
||||
a.PluginSettings[pluginName] = pluginSettings
|
||||
}
|
||||
|
||||
var err error
|
||||
if a.KubeConfig == nil {
|
||||
a.KubeConfig, err = clientcmd.BuildConfigFromFlags("", a.KubeConfigFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if a.KubeClient == nil {
|
||||
a.KubeClient, err = kubernetes.NewForConfig(a.KubeConfig)
|
||||
if err != nil {
|
||||
func (a *AirshipCTLSettings) Init() error {
|
||||
for _, pluginSettings := range a.PluginSettings {
|
||||
if err := pluginSettings.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
60
pkg/workflow/environment/settings.go
Normal file
60
pkg/workflow/environment/settings.go
Normal file
@ -0,0 +1,60 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
argo "github.com/argoproj/argo/pkg/client/clientset/versioned/typed/workflow/v1alpha1"
|
||||
|
||||
apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
|
||||
)
|
||||
|
||||
// Settings is a container for all of the settings needed by workflows
|
||||
type Settings struct {
|
||||
// Namespace is the kubernetes namespace to be used during the context of this action
|
||||
Namespace string
|
||||
|
||||
// KubeConfigFilePath is the path to the kubernetes configuration file.
|
||||
// This flag is only needed when airshipctl is being used
|
||||
// out-of-cluster
|
||||
KubeConfigFilePath string
|
||||
|
||||
// KubeClient is an instrument for interacting with a kubernetes cluster
|
||||
KubeClient kubernetes.Interface
|
||||
|
||||
// ArgoClient is an instrument for interacting with Argo workflows
|
||||
ArgoClient argo.ArgoprojV1alpha1Interface
|
||||
|
||||
CRDClient apixv1beta1.ApiextensionsV1beta1Interface
|
||||
}
|
||||
|
||||
// InitFlags adds the default settings flags to cmd
|
||||
func (s *Settings) InitFlags(cmd *cobra.Command) {
|
||||
flags := cmd.PersistentFlags()
|
||||
flags.StringVar(&s.KubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
|
||||
flags.StringVar(&s.Namespace, "namespace", "default", "kubernetes namespace to use for the context of this command")
|
||||
}
|
||||
|
||||
// Init assigns default values
|
||||
func (s *Settings) Init() error {
|
||||
if s.KubeConfigFilePath == "" {
|
||||
s.KubeConfigFilePath = clientcmd.RecommendedHomeFile
|
||||
}
|
||||
|
||||
var err error
|
||||
kubeConfig, err := clientcmd.BuildConfigFromFlags("", s.KubeConfigFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.KubeClient, err = kubernetes.NewForConfig(kubeConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.ArgoClient, err = argo.NewForConfig(kubeConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user