Move kubernetes client processes into plugins

This commit is contained in:
Ian Howell 2019-05-13 12:03:25 -05:00
parent 35ed575fbc
commit df6b11515b
7 changed files with 52 additions and 82 deletions
cmd
root.go
testdata
TestRootGoldenOutput
TestVersionGoldenOutput
version.go
internal/test
pkg/plugin
plugins/internal

@ -7,7 +7,6 @@ import (
"os"
"github.com/ian-howell/airshipctl/pkg/environment"
"github.com/ian-howell/airshipctl/pkg/kube"
"github.com/ian-howell/airshipctl/pkg/log"
"github.com/ian-howell/airshipctl/pkg/plugin"
@ -18,7 +17,7 @@ var settings environment.AirshipCTLSettings
// NewRootCmd creates the root `airshipctl` command. All other commands are
// subcommands branching from this one
func NewRootCmd(out io.Writer, client *kube.Client, 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",
@ -26,30 +25,27 @@ func NewRootCmd(out io.Writer, client *kube.Client, args []string) (*cobra.Comma
rootCmd.SetOutput(out)
// Settings flags - This section should probably be moved to pkg/environment
rootCmd.PersistentFlags().StringVar(&settings.KubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
rootCmd.PersistentFlags().BoolVar(&settings.Debug, "debug", false, "enable verbose output")
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())
}
log.Init(&settings, out)
rootCmd.AddCommand(NewVersionCommand(out, client))
workflowPlugin := "plugins/internal/workflow.so"
if _, err := os.Stat(workflowPlugin); err == nil {
rootCmd.AddCommand(plugin.CreateCommandFromPlugin(workflowPlugin, out, settings.KubeConfigFilePath))
}
return rootCmd, nil
}
// Execute runs the base airshipctl command
func Execute(out io.Writer) {
// TODO(howell): Fix this unused error
client, _ := kube.NewForConfig(settings.KubeConfigFilePath)
rootCmd, err := NewRootCmd(out, client, os.Args[1:])
rootCmd, err := NewRootCmd(out, os.Args[1:])
osExitIfError(out, err)
osExitIfError(out, rootCmd.Execute())
}

@ -5,14 +5,10 @@ Usage:
Available Commands:
help Help about any command
version Show the version number of airshipctl and its underlying tools
version Show the version number of airshipctl
Flags:
--debug enable verbose output
-h, --help help for airshipctl
--kubeconfig string path to kubeconfig
Additional help topics:
airshipctl workflow access to workflows
--debug enable verbose output
-h, --help help for airshipctl
Use "airshipctl [command] --help" for more information about a command.

@ -1,2 +1 @@
client: v0.1.0
kubernetes server: v0.0.0-master+$Format:%h$
airshipctl: v0.1.0

@ -6,34 +6,18 @@ import (
"text/tabwriter"
"github.com/spf13/cobra"
kube "github.com/ian-howell/airshipctl/pkg/kube"
)
const versionLong = `Show the versions for the airshipctl tool and its components.
This includes the following components, in order:
* airshipctl client
* kubernetes cluster
`
// NewVersionCommand prints out the versions of airshipctl and its underlying tools
func NewVersionCommand(out io.Writer, client *kube.Client) *cobra.Command {
func NewVersionCommand(out io.Writer) *cobra.Command {
versionCmd := &cobra.Command{
Use: "version",
Short: "Show the version number of airshipctl and its underlying tools",
Long: versionLong,
Short: "Show the version number of airshipctl",
Run: func(cmd *cobra.Command, args []string) {
clientV := clientVersion()
kubeV, err := kubeVersion(client)
if err != nil {
fmt.Fprintf(out, "Could not get kubernetes version")
return
}
w := tabwriter.NewWriter(out, 0, 0, 1, ' ', 0)
defer w.Flush()
fmt.Fprintf(w, "%s:\t%s\n", "client", clientV)
fmt.Fprintf(w, "%s:\t%s\n", "kubernetes server", kubeV)
fmt.Fprintf(w, "%s:\t%s\n", "airshipctl", clientV)
},
}
return versionCmd
@ -43,14 +27,3 @@ func clientVersion() string {
// TODO(howell): There's gotta be a smarter way to do this
return "v0.1.0"
}
func kubeVersion(client *kube.Client) (string, error) {
if client == nil {
return "could not connect to a kubernetes cluster", nil
}
version, err := client.Discovery().ServerVersion()
if err != nil {
return "", err
}
return version.String(), nil
}

@ -10,8 +10,6 @@ import (
"testing"
"github.com/ian-howell/airshipctl/cmd"
"github.com/ian-howell/airshipctl/pkg/kube"
"k8s.io/client-go/kubernetes/fake"
)
// UpdateGolden writes out the golden files with the latest values, rather than failing the test.
@ -42,10 +40,9 @@ func RunCmdTests(t *testing.T, tests []CmdTest) {
func executeCmd(t *testing.T, command string) []byte {
var actual bytes.Buffer
client := &kube.Client{Interface: fake.NewSimpleClientset()}
// TODO(howell): switch to shellwords (or similar)
args := strings.Fields(command)
rootCmd, err := cmd.NewRootCmd(&actual, client, args)
rootCmd, err := cmd.NewRootCmd(&actual, args)
if err != nil {
t.Fatalf(err.Error())
}

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
)
func CreateCommandFromPlugin(pluginPath string, out io.Writer, configPath string) *cobra.Command {
func CreateCommandFromPlugin(pluginPath string, out io.Writer, args []string) *cobra.Command {
//TODO(howell): Remove these panics
plug, err := plugin.Open(pluginPath)
if err != nil {
@ -17,9 +17,9 @@ func CreateCommandFromPlugin(pluginPath string, out io.Writer, configPath string
if err != nil {
panic(err.Error())
}
command, ok := cmdSym.(func(io.Writer, string) *cobra.Command)
command, ok := cmdSym.(func(io.Writer, []string) *cobra.Command)
if !ok {
panic("NewCommand does not meet the interface.")
}
return command(out, configPath)
return command(out, args)
}

@ -13,46 +13,55 @@ import (
"k8s.io/client-go/tools/clientcmd"
)
func NewCommand(out io.Writer, configPath string) *cobra.Command {
//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.AddCommand(NewWorkflowListCommand(out, configPath))
workflowRootCmd.PersistentFlags().StringVar(&kubeConfigFilePath, "kubeconfig", "", "path to kubeconfig")
workflowRootCmd.AddCommand(NewWorkflowListCommand(out, args))
return workflowRootCmd
}
func NewWorkflowListCommand(out io.Writer, configPath string) *cobra.Command {
//nolint:unused
func NewWorkflowListCommand(out io.Writer, args []string) *cobra.Command {
config, err := clientcmd.BuildConfigFromFlags("", configPath)
if err != nil {
panic(err.Error())
}
v1alpha1.AddToScheme(scheme.Scheme)
crdConfig := *config
crdConfig.ContentConfig.GroupVersion = &v1alpha1.SchemeGroupVersion
crdConfig.APIPath = "/apis"
crdConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
crdConfig.UserAgent = rest.DefaultKubernetesUserAgent()
exampleRestClient, err := rest.UnversionedRESTClientFor(&crdConfig)
if err != nil {
panic(err)
}
// TODO(howell): This is only used to appease the linter. It will be used later
_ = args
workflowRootCmd := &cobra.Command{
Use: "list",
Short: "list workflows",
Aliases: []string{"ls"},
Run: func(cmd *cobra.Command, args []string) {
wflist := v1alpha1.WorkflowList{}
if kubeConfigFilePath == "" {
kubeConfigFilePath = clientcmd.RecommendedHomeFile
}
config, err := clientcmd.BuildConfigFromFlags("", kubeConfigFilePath)
if err != nil {
panic(err.Error())
}
err := exampleRestClient.
crdConfig := *config
crdConfig.ContentConfig.GroupVersion = &v1alpha1.SchemeGroupVersion
crdConfig.APIPath = "/apis"
crdConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
crdConfig.UserAgent = rest.DefaultKubernetesUserAgent()
exampleRestClient, err := rest.UnversionedRESTClientFor(&crdConfig)
if err != nil {
panic(err)
}
wflist := v1alpha1.WorkflowList{}
err = exampleRestClient.
Get().
Resource("workflows").
Do().