diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 0000000..4c6aaef --- /dev/null +++ b/cmd/list.go @@ -0,0 +1,131 @@ +/* +Package cmd list existing karbon cluster(s) +Copyright © 2021 Christophe Jauffret + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/user" + "text/tabwriter" + "time" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// listCmd represents the list command +var listCmd = &cobra.Command{ + Use: "list", + Short: "Get the list of k8s clusters", + Long: `Return the list of all kubernetes cluster running on the tergeted Nutanix Karbon platform`, + PreRun: func(cmd *cobra.Command, args []string) { + + viper.BindPFlag("server", cmd.Flags().Lookup("server")) + viper.BindPFlag("user", cmd.Flags().Lookup("user")) + viper.BindPFlag("port", cmd.Flags().Lookup("port")) + viper.BindPFlag("insecure", cmd.Flags().Lookup("insecure")) + }, + Run: func(cmd *cobra.Command, args []string) { + + server := viper.GetString("server") + if server == "" { + fmt.Fprintln(os.Stderr, "Error: required flag(s) \"server\" not set") + cmd.Usage() + return + } + + port := viper.GetInt("port") + + karbonListUrl := fmt.Sprintf("https://%s:%d/karbon/v1-beta.1/k8s/clusters", server, port) + method := "GET" + + if verbose { + fmt.Printf("Connect on https://%s:%d/ and retrieve cluster list\n", server, port) + } + + insecureSkipVerify := viper.GetBool("insecure") + + customTransport := http.DefaultTransport.(*http.Transport).Clone() + customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: insecureSkipVerify} + + timeout, _ := cmd.Flags().GetInt("request-timeout") + client := &http.Client{Transport: customTransport, Timeout: time.Second * time.Duration(timeout)} + req, err := http.NewRequest(method, karbonListUrl, nil) + cobra.CheckErr(err) + + userArg, password := getCredentials() + + req.SetBasicAuth(userArg, password) + + res, err := client.Do(req) + cobra.CheckErr(err) + + defer res.Body.Close() + + switch res.StatusCode { + case 401: + fmt.Println("Invalid client credentials") + return + case 200: + // OK + default: + fmt.Println("Internal Error") + return + + } + + body, err := ioutil.ReadAll(res.Body) + cobra.CheckErr(err) + + clusters := make([]map[string]interface{}, 0) + json.Unmarshal([]byte(body), &clusters) + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 0, '\t', 0) + + defer w.Flush() + + fmt.Fprintf(w, "%s\t%s\t%s\t", "NAME", "VERSION", "STATUS") + + for _, cluster := range clusters { + fmt.Fprintf(w, "\n%s\tv%s\t%s\t", cluster["name"], cluster["version"], cluster["status"].(string)[1:]) + } + fmt.Fprintf(w, "\n") + + }, +} + +func init() { + rootCmd.AddCommand(listCmd) + + user, err := user.Current() + if err != nil { + panic(err) + } + + listCmd.Flags().String("server", "", "Address of the PC to authenticate against") + + listCmd.Flags().StringP("user", "u", user.Username, "Username to authenticate") + + listCmd.Flags().Int("port", 9440, "Port to run Application server on") + + listCmd.Flags().BoolP("insecure", "k", false, "Skip certificate verification (this is insecure)") +} diff --git a/cmd/login.go b/cmd/login.go index 2194e18..f889443 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -25,12 +25,10 @@ import ( "os" "os/user" "path/filepath" - "syscall" "time" "github.com/spf13/cobra" "github.com/spf13/viper" - "golang.org/x/term" ) // flags variable @@ -41,7 +39,15 @@ var kubeconfigResponseJSON map[string]interface{} var loginCmd = &cobra.Command{ Use: "login", Short: "Authenticate user with Nutanix Prism Central", - Long: `Authenticate user with Nutanix Prism Central and create a local kubeconfig file for the selected cluster`, + Long: `Authenticate user with Nutanix Prism Central and create a local kubeconfig file for the selected cluster`, + PreRun: func(cmd *cobra.Command, args []string) { + + viper.BindPFlag("server", cmd.Flags().Lookup("server")) + viper.BindPFlag("cluster", cmd.Flags().Lookup("cluster")) + viper.BindPFlag("user", cmd.Flags().Lookup("user")) + viper.BindPFlag("port", cmd.Flags().Lookup("port")) + viper.BindPFlag("insecure", cmd.Flags().Lookup("insecure")) + }, Run: func(cmd *cobra.Command, args []string) { server := viper.GetString("server") @@ -60,7 +66,7 @@ var loginCmd = &cobra.Command{ port := viper.GetInt("port") - url := fmt.Sprintf("https://%s:%d/karbon/v1/k8s/clusters/%s/kubeconfig", server, port, cluster) + karbonKubeconfigUrl := fmt.Sprintf("https://%s:%d/karbon/v1/k8s/clusters/%s/kubeconfig", server, port, cluster) method := "GET" if verbose { @@ -74,22 +80,10 @@ var loginCmd = &cobra.Command{ timeout, _ := cmd.Flags().GetInt("request-timeout") client := &http.Client{Transport: customTransport, Timeout: time.Second * time.Duration(timeout)} - req, err := http.NewRequest(method, url, nil) + req, err := http.NewRequest(method, karbonKubeconfigUrl, nil) cobra.CheckErr(err) - userArg := viper.GetString("user") - - var password string - var ok bool - password, ok = os.LookupEnv("KARBON_PASSWORD") - - if !ok { - fmt.Printf("Enter %s password:\n", userArg) - bytePassword, err := term.ReadPassword(int(syscall.Stdin)) - cobra.CheckErr(err) - - password = string(bytePassword) - } + userArg, password := getCredentials() req.SetBasicAuth(userArg, password) @@ -157,17 +151,12 @@ func init() { } loginCmd.Flags().String("server", "", "Address of the PC to authenticate against") - viper.BindPFlag("server", loginCmd.Flags().Lookup("server")) loginCmd.Flags().StringP("user", "u", user.Username, "Username to authenticate") - viper.BindPFlag("user", loginCmd.Flags().Lookup("user")) loginCmd.Flags().String("cluster", "", "Karbon cluster to connect against") - viper.BindPFlag("cluster", loginCmd.Flags().Lookup("cluster")) loginCmd.Flags().Int("port", 9440, "Port to run Application server on") - viper.BindPFlag("port", loginCmd.Flags().Lookup("port")) loginCmd.Flags().BoolP("insecure", "k", false, "Skip certificate verification (this is insecure)") - viper.BindPFlag("insecure", loginCmd.Flags().Lookup("insecure")) } diff --git a/cmd/root.go b/cmd/root.go index 9dfbc4c..c60bb7b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,8 +19,10 @@ package cmd import ( "fmt" "os" + "syscall" "github.com/spf13/cobra" + "golang.org/x/term" "github.com/spf13/viper" ) @@ -93,6 +95,25 @@ func initConfig() { // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + if verbose { + fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + } } } + +func getCredentials() (string, string) { + userArg := viper.GetString("user") + + var password string + var ok bool + password, ok = os.LookupEnv("KARBON_PASSWORD") + + if !ok { + fmt.Printf("Enter %s password:\n", userArg) + bytePassword, err := term.ReadPassword(int(syscall.Stdin)) + cobra.CheckErr(err) + + password = string(bytePassword) + } + return userArg, password +}