From 32b397e1729d509f65440a6287a51e0e66d9d153 Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Wed, 2 May 2018 13:26:25 -0700 Subject: [PATCH] operator-sdk/cmd: add up command --- commands/operator-sdk/cmd/build.go | 12 +--- commands/operator-sdk/cmd/cmdutil/util.go | 37 +++++++++++ commands/operator-sdk/cmd/root.go | 1 + commands/operator-sdk/cmd/up.go | 19 ++++++ commands/operator-sdk/cmd/up/local.go | 78 +++++++++++++++++++++++ pkg/k8sclient/client.go | 5 +- pkg/util/k8sutil/constants.go | 7 ++ 7 files changed, 147 insertions(+), 12 deletions(-) create mode 100644 commands/operator-sdk/cmd/cmdutil/util.go create mode 100644 commands/operator-sdk/cmd/up.go create mode 100644 commands/operator-sdk/cmd/up/local.go create mode 100644 pkg/util/k8sutil/constants.go diff --git a/commands/operator-sdk/cmd/build.go b/commands/operator-sdk/cmd/build.go index 69364c5fbdc..e67dae1a7e3 100644 --- a/commands/operator-sdk/cmd/build.go +++ b/commands/operator-sdk/cmd/build.go @@ -16,15 +16,14 @@ package cmd import ( "fmt" - "io/ioutil" "os" "os/exec" + "github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/cmdutil" cmdError "github.com/operator-framework/operator-sdk/commands/operator-sdk/error" "github.com/operator-framework/operator-sdk/pkg/generator" "github.com/spf13/cobra" - yaml "gopkg.in/yaml.v2" ) func NewBuildCmd() *cobra.Command { @@ -74,14 +73,7 @@ func buildFunc(cmd *cobra.Command, args []string) { } fmt.Fprintln(os.Stdout, string(o)) - c := &generator.Config{} - fp, err := ioutil.ReadFile(configYaml) - if err != nil { - cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to read config file %v: (%v)", configYaml, err)) - } - if err = yaml.Unmarshal(fp, c); err != nil { - cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to unmarshal config file %v: (%v)", configYaml, err)) - } + c := cmdutil.GetConfig() if err = generator.RenderOperatorYaml(c, image); err != nil { cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to generate deploy/operator.yaml: (%v)", err)) } diff --git a/commands/operator-sdk/cmd/cmdutil/util.go b/commands/operator-sdk/cmd/cmdutil/util.go new file mode 100644 index 00000000000..3894f145d63 --- /dev/null +++ b/commands/operator-sdk/cmd/cmdutil/util.go @@ -0,0 +1,37 @@ +package cmdutil + +import ( + "fmt" + "io/ioutil" + "os" + + cmdError "github.com/operator-framework/operator-sdk/commands/operator-sdk/error" + "github.com/operator-framework/operator-sdk/pkg/generator" + + yaml "gopkg.in/yaml.v2" +) + +const configYaml = "./config/config.yaml" + +// MustInProjectRoot checks if the current dir is the project root. +func MustInProjectRoot() { + // if the current directory has the "./config/config.yaml" file, then it is safe to say + // we are at the project root. + _, err := os.Stat(configYaml) + if err != nil && os.IsNotExist(err) { + cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("must in project root dir: %v", err)) + } +} + +// GetConfig gets the values from ./config/config.yaml and parses them into a Config struct. +func GetConfig() *generator.Config { + c := &generator.Config{} + fp, err := ioutil.ReadFile(configYaml) + if err != nil { + cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to read config file %v: (%v)", configYaml, err)) + } + if err = yaml.Unmarshal(fp, c); err != nil { + cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to unmarshal config file %v: (%v)", configYaml, err)) + } + return c +} diff --git a/commands/operator-sdk/cmd/root.go b/commands/operator-sdk/cmd/root.go index 2f09596d755..4424872b0d3 100644 --- a/commands/operator-sdk/cmd/root.go +++ b/commands/operator-sdk/cmd/root.go @@ -25,6 +25,7 @@ func NewRootCmd() *cobra.Command { cmd.AddCommand(NewNewCmd()) cmd.AddCommand(NewBuildCmd()) cmd.AddCommand(NewGenerateCmd()) + cmd.AddCommand(NewUpCmd()) return cmd } diff --git a/commands/operator-sdk/cmd/up.go b/commands/operator-sdk/cmd/up.go new file mode 100644 index 00000000000..3549f4f8968 --- /dev/null +++ b/commands/operator-sdk/cmd/up.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/up" + + "github.com/spf13/cobra" +) + +func NewUpCmd() *cobra.Command { + upCmd := &cobra.Command{ + Use: "up", + Short: "Launches the operator", + Long: `The up command has subcommands that can launch the operator in various ways. +`, + } + + upCmd.AddCommand(up.NewLocalCmd()) + return upCmd +} diff --git a/commands/operator-sdk/cmd/up/local.go b/commands/operator-sdk/cmd/up/local.go new file mode 100644 index 00000000000..64fac65ab89 --- /dev/null +++ b/commands/operator-sdk/cmd/up/local.go @@ -0,0 +1,78 @@ +package up + +import ( + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + + "github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/cmdutil" + cmdError "github.com/operator-framework/operator-sdk/commands/operator-sdk/error" + "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" + + "github.com/spf13/cobra" +) + +func NewLocalCmd() *cobra.Command { + upLocalCmd := &cobra.Command{ + Use: "local", + Short: "Launches the operator locally", + Long: `The operator-sdk up local command launches the operator on the local machine +by building the operator binary with the ability to access a +kubernetes cluster using a kubeconfig file. +`, + Run: upLocalFunc, + } + + upLocalCmd.Flags().StringVar(&kubeConfig, "kubeconfig", "", "The file path to kubernetes configuration file; defaults to $HOME/.kube/config") + + return upLocalCmd +} + +var ( + kubeConfig string +) + +const ( + gocmd = "go" + run = "run" + cmd = "cmd" + main = "main.go" + defaultConfigPath = ".kube/config" +) + +func upLocalFunc(cmd *cobra.Command, args []string) { + mustKubeConfig() + cmdutil.MustInProjectRoot() + c := cmdutil.GetConfig() + upLocal(c.ProjectName) +} + +// mustKubeConfig checks if the kubeconfig file exists. +func mustKubeConfig() { + // if kubeConfig is not specified, search for the default kubeconfig file under the $HOME/.kube/config. + if len(kubeConfig) == 0 { + usr, err := user.Current() + if err != nil { + cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to determine user's home dir: %v", err)) + } + kubeConfig = filepath.Join(usr.HomeDir, defaultConfigPath) + } + + _, err := os.Stat(kubeConfig) + if err != nil && os.IsNotExist(err) { + cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to find the kubeconfig file (%v): %v", kubeConfig, err)) + } +} + +func upLocal(projectName string) { + dc := exec.Command(gocmd, run, filepath.Join(cmd, projectName, main)) + dc.Stdout = os.Stdout + dc.Stderr = os.Stderr + dc.Env = append(os.Environ(), fmt.Sprintf("%v=%v", k8sutil.KubeConfigEnvVar, kubeConfig)) + err := dc.Run() + if err != nil { + cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to run operator locally: %v", err)) + } +} diff --git a/pkg/k8sclient/client.go b/pkg/k8sclient/client.go index 86be64a2eb6..fed639a84c7 100644 --- a/pkg/k8sclient/client.go +++ b/pkg/k8sclient/client.go @@ -19,6 +19,7 @@ import ( "net" "os" + "github.com/operator-framework/operator-sdk/pkg/util/k8sutil" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -89,7 +90,7 @@ func apiResource(gvk schema.GroupVersionKind, restMapper *discovery.DeferredDisc func mustNewKubeClientAndConfig() (kubernetes.Interface, *rest.Config) { var cfg *rest.Config var err error - if os.Getenv("KUBERNETES_CONFIG") != "" { + if os.Getenv(KubeConfigEnvVar) != "" { cfg, err = outOfClusterConfig() } else { cfg, err = inClusterConfig() @@ -118,7 +119,7 @@ func inClusterConfig() (*rest.Config, error) { } func outOfClusterConfig() (*rest.Config, error) { - kubeconfig := os.Getenv("KUBERNETES_CONFIG") + kubeconfig := os.Getenv(k8sutil.KubeConfigEnvVar) config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) return config, err } diff --git a/pkg/util/k8sutil/constants.go b/pkg/util/k8sutil/constants.go new file mode 100644 index 00000000000..77f1e6215c2 --- /dev/null +++ b/pkg/util/k8sutil/constants.go @@ -0,0 +1,7 @@ +package k8sutil + +const ( + // KubeConfigEnvVar defines the env variable KUBERNETES_CONFIG which + // contains the kubeconfig file path. + KubeConfigEnvVar = "KUBERNETES_CONFIG" +)