Move the client objects out of the settings objects

* This also modifies the way that logging is handled
This commit is contained in:
Ian Howell 2019-06-06 10:57:33 -05:00
parent 247a439abc
commit cbd1e048c9
11 changed files with 126 additions and 146 deletions

View File

@ -1,7 +1,6 @@
package cmd
import (
"fmt"
"io"
"github.com/spf13/cobra"
@ -9,6 +8,7 @@ import (
"github.com/ian-howell/airshipctl/cmd/bootstrap"
"github.com/ian-howell/airshipctl/cmd/workflow"
"github.com/ian-howell/airshipctl/pkg/environment"
"github.com/ian-howell/airshipctl/pkg/log"
)
// NewRootCmd creates the root `airshipctl` command. All other commands are
@ -20,11 +20,8 @@ func NewRootCmd(out io.Writer) (*cobra.Command, *environment.AirshipCTLSettings,
Short: "airshipctl is a unified entrypoint to various airship components",
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := settings.Init(); err != nil {
return fmt.Errorf("error while initializing settings: %s", err)
}
return nil
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.Init(settings.Debug, cmd.OutOrStderr())
},
}
rootCmd.SetOutput(out)

View File

@ -18,12 +18,13 @@ func NewWorkflowCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Com
Aliases: []string{"workflows", "wf"},
}
wfSettings := &wfenv.Settings{}
wfSettings := &wfenv.Settings{
AirshipCTLSettings: rootSettings,
}
wfSettings.InitFlags(workflowRootCmd)
rootSettings.Register(PluginSettingsID, wfSettings)
workflowRootCmd.AddCommand(NewWorkflowInitCommand(rootSettings))
workflowRootCmd.AddCommand(NewWorkflowListCommand(rootSettings))
workflowRootCmd.AddCommand(NewWorkflowInitCommand(wfSettings))
workflowRootCmd.AddCommand(NewWorkflowListCommand(wfSettings))
return workflowRootCmd
}

View File

@ -5,9 +5,8 @@ import (
"github.com/spf13/cobra"
"github.com/ian-howell/airshipctl/pkg/environment"
"github.com/ian-howell/airshipctl/pkg/workflow"
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
wf "github.com/ian-howell/airshipctl/pkg/workflow"
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
)
var (
@ -15,20 +14,21 @@ var (
)
// NewWorkflowInitCommand is a command for bootstrapping a kubernetes cluster with the necessary components for Argo workflows
func NewWorkflowInitCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
func NewWorkflowInitCommand(settings *environment.Settings) *cobra.Command {
workflowInitCommand := &cobra.Command{
Use: "init [flags]",
Short: "bootstraps the kubernetes cluster with the Workflow CRDs and controller",
Run: func(cmd *cobra.Command, args []string) {
out := cmd.OutOrStdout()
wfSettings, ok := rootSettings.PluginSettings[PluginSettingsID].(*wfenv.Settings)
if !ok {
fmt.Fprintf(out, "settings for %s were not registered\n", PluginSettingsID)
clientset, err := wf.GetClientset(settings)
if err != nil {
fmt.Fprintf(out, "Could not get Workflow Clientset: %s\n", err.Error())
return
}
if err := workflow.Initialize(out, wfSettings, manifestPath); err != nil {
if err := wf.Initialize(clientset, settings, manifestPath); err != nil {
fmt.Fprintf(out, "error while initializing argo: %s\n", err.Error())
return
}

View File

@ -8,32 +8,32 @@ import (
"github.com/spf13/cobra"
"github.com/ian-howell/airshipctl/pkg/apis/workflow/v1alpha1"
"github.com/ian-howell/airshipctl/pkg/environment"
"github.com/ian-howell/airshipctl/pkg/util"
"github.com/ian-howell/airshipctl/pkg/workflow"
wfenv "github.com/ian-howell/airshipctl/pkg/workflow/environment"
wf "github.com/ian-howell/airshipctl/pkg/workflow"
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
wfutil "github.com/ian-howell/airshipctl/pkg/workflow/util"
)
// NewWorkflowListCommand is a command for listing argo workflows
func NewWorkflowListCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
func NewWorkflowListCommand(settings *environment.Settings) *cobra.Command {
workflowListCmd := &cobra.Command{
Use: "list",
Short: "list workflows",
Aliases: []string{"ls"},
Run: func(cmd *cobra.Command, args []string) {
out := cmd.OutOrStdout()
wfSettings, ok := rootSettings.PluginSettings[PluginSettingsID].(*wfenv.Settings)
if !ok {
fmt.Fprintf(out, "settings for %s were not registered\n", PluginSettingsID)
fmt.Fprintf(out, "debug state: %v\n", settings.Debug)
clientset, err := wf.GetClientset(settings)
if err != nil {
fmt.Fprintf(out, "Could not get Workflow Clientset: %s\n", err.Error())
return
}
wflist, err := workflow.ListWorkflows(wfSettings)
wflist, err := wf.ListWorkflows(clientset, settings)
if err != nil {
fmt.Fprintf(out, "Could not list workflows: %s\n", err.Error())
return
}
printTable(out, wflist, wfSettings)
printTable(out, wflist, settings)
},
}
@ -41,10 +41,10 @@ func NewWorkflowListCommand(rootSettings *environment.AirshipCTLSettings) *cobra
}
// printTable pretty prints the list of workflows to out
func printTable(out io.Writer, wfList []v1alpha1.Workflow, wfSettings *wfenv.Settings) {
func printTable(out io.Writer, wfList []v1alpha1.Workflow, settings *environment.Settings) {
w := util.NewTabWriter(out)
defer w.Flush()
if wfSettings.AllNamespaces {
if settings.AllNamespaces {
fmt.Fprint(w, "NAMESPACE\t")
}
fmt.Fprint(w, "NAME\tSTATUS\tAGE\tDURATION\tPRIORITY")
@ -52,7 +52,7 @@ func printTable(out io.Writer, wfList []v1alpha1.Workflow, wfSettings *wfenv.Set
for _, wf := range wfList {
ageStr := humanize.RelativeDurationShort(wf.ObjectMeta.CreationTimestamp.Time, util.Now())
durationStr := humanize.RelativeDurationShort(wf.Status.StartedAt.Time, wf.Status.FinishedAt.Time)
if wfSettings.AllNamespaces {
if settings.AllNamespaces {
fmt.Fprintf(w, "%s\t", wf.ObjectMeta.Namespace)
}
var priority int

View File

@ -5,7 +5,6 @@ import (
"os"
"github.com/ian-howell/airshipctl/cmd"
"github.com/ian-howell/airshipctl/pkg/log"
)
func main() {
@ -17,8 +16,6 @@ func main() {
cmd.AddDefaultAirshipCTLCommands(rootCmd, settings)
log.Init(settings, os.Stdout)
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stdout, err)
os.Exit(1)

View File

@ -4,16 +4,10 @@ import (
"github.com/spf13/cobra"
)
type PluginSettings interface {
Init() error
}
// AirshipCTLSettings is a container for all of the settings needed by airshipctl
type AirshipCTLSettings struct {
// Debug is used for verbose output
Debug bool
PluginSettings map[string]PluginSettings
}
// InitFlags adds the default settings flags to cmd
@ -21,19 +15,3 @@ func (a *AirshipCTLSettings) InitFlags(cmd *cobra.Command) {
flags := cmd.PersistentFlags()
flags.BoolVar(&a.Debug, "debug", false, "enable verbose output")
}
func (a *AirshipCTLSettings) Register(pluginName string, pluginSettings PluginSettings) {
if a.PluginSettings == nil {
a.PluginSettings = map[string]PluginSettings{}
}
a.PluginSettings[pluginName] = pluginSettings
}
func (a *AirshipCTLSettings) Init() error {
for _, pluginSettings := range a.PluginSettings {
if err := pluginSettings.Init(); err != nil {
return err
}
}
return nil
}

View File

@ -3,15 +3,13 @@ package log
import (
"io"
"log"
"github.com/ian-howell/airshipctl/pkg/environment"
)
var debug = false
// Init initializes settings related to logging
func Init(settings *environment.AirshipCTLSettings, out io.Writer) {
debug = settings.Debug
func Init(debugFlag bool, out io.Writer) {
debug = debugFlag
log.SetOutput(out)
}

60
pkg/workflow/clientset.go Normal file
View File

@ -0,0 +1,60 @@
package workflow
import (
apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
argo "github.com/ian-howell/airshipctl/pkg/client/clientset/versioned"
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
)
// Clientset is a container for the various clients that are useful to the workflow command
type Clientset struct {
// Kube is an instrument for interacting with a kubernetes cluster
Kube kubernetes.Interface
// Argo is an instrument for interacting with Argo workflows
Argo argo.Interface
// CRD is an instrument for interacting with CRDs
CRD apixv1beta1.Interface
}
var (
clientset *Clientset
)
// GetClientset provides access to the clientset singleton
func GetClientset(settings *environment.Settings) (*Clientset, error) {
if clientset != nil {
return clientset, nil
}
if settings.KubeConfigFilePath == "" {
settings.KubeConfigFilePath = clientcmd.RecommendedHomeFile
}
kubeConfig, err := clientcmd.BuildConfigFromFlags("", settings.KubeConfigFilePath)
if err != nil {
return nil, err
}
clientset.Kube, err = kubernetes.NewForConfig(kubeConfig)
if err != nil {
return nil, err
}
clientset.Argo, err = argo.NewForConfig(kubeConfig)
if err != nil {
return nil, err
}
clientset.CRD, err = apixv1beta1.NewForConfig(kubeConfig)
if err != nil {
return nil, err
}
clientset = &Clientset{}
return clientset, nil
}

View File

@ -2,15 +2,14 @@ package environment
import (
"github.com/spf13/cobra"
apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
argo "github.com/ian-howell/airshipctl/pkg/client/clientset/versioned"
"github.com/ian-howell/airshipctl/pkg/environment"
)
// Settings is a container for all of the settings needed by workflows
type Settings struct {
*environment.AirshipCTLSettings
// Namespace is the kubernetes namespace to be used during the context of this action
Namespace string
@ -21,18 +20,6 @@ type Settings struct {
// 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.Interface
// CRDClient is an instrument for interacting with CRDs
CRDClient apixv1beta1.Interface
// Initialized denotes whether the settings have been initialized or not. It is useful for unit-testing
Initialized bool
}
// InitFlags adds the default settings flags to cmd
@ -42,38 +29,3 @@ func (s *Settings) InitFlags(cmd *cobra.Command) {
flags.StringVar(&s.Namespace, "namespace", "default", "kubernetes namespace to use for the context of this command")
flags.BoolVar(&s.AllNamespaces, "all-namespaces", false, "use all kubernetes namespaces for the context of this command")
}
// Init assigns default values
func (s *Settings) Init() error {
if s.Initialized {
return nil
}
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
}
s.CRDClient, err = apixv1beta1.NewForConfig(kubeConfig)
if err != nil {
return err
}
s.Initialized = true
return nil
}

View File

@ -2,7 +2,6 @@ package workflow
import (
"fmt"
"io"
v1beta2 "k8s.io/api/apps/v1beta2"
"k8s.io/api/core/v1"
@ -13,6 +12,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"github.com/ian-howell/airshipctl/pkg/log"
"github.com/ian-howell/airshipctl/pkg/workflow/environment"
)
@ -22,16 +22,13 @@ const (
// Initialize creates the argo namespace and all of the required kubernetes
// objects for argo to function
func Initialize(out io.Writer, settings *environment.Settings, manifestPath string) error {
kubeClient := settings.KubeClient
crdClient := settings.CRDClient
if err := createNamespace(out, kubeClient); err != nil {
func Initialize(clientset *Clientset, settings *environment.Settings, manifestPath string) error {
if err := createNamespace(clientset.Kube); err != nil {
return err
}
if manifestPath == "" {
if err := createDefaultObjects(out, kubeClient, crdClient); err != nil {
if err := createDefaultObjects(clientset.Kube, clientset.CRD); err != nil {
return fmt.Errorf("could not create default objects: %s", err.Error())
}
} else {
@ -43,55 +40,55 @@ func Initialize(out io.Writer, settings *environment.Settings, manifestPath stri
return nil
}
func createNamespace(out io.Writer, kubeClient kubernetes.Interface) error {
fmt.Fprintf(out, "Creating namespace %s\n", argoNamespace)
func createNamespace(kubeClient kubernetes.Interface) error {
log.Debugf("Creating namespace %s", argoNamespace)
_, err := kubeClient.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "argo"}})
return handleCreateError(out, fmt.Sprintf("namespace %s", argoNamespace), err)
return handleCreateError(fmt.Sprintf("namespace %s", argoNamespace), err)
}
func createDefaultObjects(out io.Writer, kubeClient kubernetes.Interface, crdClient apixv1beta1client.Interface) error {
fmt.Fprintf(out, "Registering Workflow CRD\n")
if err := handleCreateError(out, "Workflow CRD", registerDefaultWorkflow(crdClient)); err != nil {
func createDefaultObjects(kubeClient kubernetes.Interface, crdClient apixv1beta1client.Interface) error {
log.Debugf("Registering Workflow CRD")
if err := handleCreateError("Workflow CRD", registerDefaultWorkflow(crdClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ServiceAccount\n")
if err := handleCreateError(out, "argo ServiceAccount", createArgoServiceAccount(kubeClient)); err != nil {
log.Debugf("Creating argo ServiceAccount")
if err := handleCreateError("argo ServiceAccount", createArgoServiceAccount(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo admin ClusterRole\n")
if err := handleCreateError(out, "argo admin ClusterRole", createArgoAdminClusterRole(kubeClient)); err != nil {
log.Debugf("Creating argo admin ClusterRole")
if err := handleCreateError("argo admin ClusterRole", createArgoAdminClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo edit ClusterRole\n")
if err := handleCreateError(out, "argo edit ClusterRole", createArgoEditClusterRole(kubeClient)); err != nil {
log.Debugf("Creating argo edit ClusterRole")
if err := handleCreateError("argo edit ClusterRole", createArgoEditClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo view ClusterRole\n")
if err := handleCreateError(out, "argo view ClusterRole", createArgoViewClusterRole(kubeClient)); err != nil {
log.Debugf("Creating argo view ClusterRole")
if err := handleCreateError("argo view ClusterRole", createArgoViewClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ClusterRole\n")
if err := handleCreateError(out, "argo ClusterRole", createArgoClusterRole(kubeClient)); err != nil {
log.Debugf("Creating argo ClusterRole")
if err := handleCreateError("argo ClusterRole", createArgoClusterRole(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ClusterRoleBinding\n")
if err := handleCreateError(out, "argo ClusterRoleBinding", createArgoClusterRoleBinding(kubeClient)); err != nil {
log.Debugf("Creating argo ClusterRoleBinding")
if err := handleCreateError("argo ClusterRoleBinding", createArgoClusterRoleBinding(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo ConfigMap\n")
if err := handleCreateError(out, "argo ConfigMap", createArgoConfigMap(kubeClient)); err != nil {
log.Debugf("Creating argo ConfigMap")
if err := handleCreateError("argo ConfigMap", createArgoConfigMap(kubeClient)); err != nil {
return err
}
fmt.Fprintf(out, "Creating argo Deployment\n")
if err := handleCreateError(out, "argo Deployment", createArgoDeployment(kubeClient)); err != nil {
log.Debugf("Creating argo Deployment")
if err := handleCreateError("argo Deployment", createArgoDeployment(kubeClient)); err != nil {
return err
}
@ -380,12 +377,12 @@ func createArgoDeployment(kubeClient kubernetes.Interface) error {
return err
}
func handleCreateError(out io.Writer, resourceName string, err error) error {
func handleCreateError(resourceName string, err error) error {
if err == nil {
return nil
}
if errors.IsAlreadyExists(err) {
fmt.Fprintf(out, "%s already exists\n", resourceName)
log.Debugf("%s already exists", resourceName)
return nil
}
return fmt.Errorf("Could not create %s: %s", resourceName, err.Error())

View File

@ -12,14 +12,14 @@ import (
)
// ListWorkflows returns a list of Workflows
func ListWorkflows(settings *environment.Settings) ([]v1alpha1.Workflow, error) {
var clientSet v1alpha1client.WorkflowInterface
func ListWorkflows(clientset *Clientset, settings *environment.Settings) ([]v1alpha1.Workflow, error) {
var wfClient v1alpha1client.WorkflowInterface
if settings.AllNamespaces {
clientSet = settings.ArgoClient.ArgoprojV1alpha1().Workflows(apiv1.NamespaceAll)
wfClient = clientset.Argo.ArgoprojV1alpha1().Workflows(apiv1.NamespaceAll)
} else {
clientSet = settings.ArgoClient.ArgoprojV1alpha1().Workflows(settings.Namespace)
wfClient = clientset.Argo.ArgoprojV1alpha1().Workflows(settings.Namespace)
}
wflist, err := clientSet.List(v1.ListOptions{})
wflist, err := wfClient.List(v1.ListOptions{})
if err != nil {
return []v1alpha1.Workflow{}, err
}