diff --git a/README.md b/README.md index 6677aae..00a92b6 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Use "pf9ctl [command] --help" for more information about a command. ```sh #pf9ctl version -pf9ctl version: v1.15 +pf9ctl version: v1.16 ``` - **Upgrading** diff --git a/cmd/attachNode.go b/cmd/attachNode.go index 48300b6..e1834a3 100644 --- a/cmd/attachNode.go +++ b/cmd/attachNode.go @@ -58,17 +58,11 @@ func attachNodeRun(cmd *cobra.Command, args []string) { detachedMode := cmd.Flags().Changed("no-prompt") - if cmdexec.CheckRemote(nc) { - if !config.ValidateNodeConfig(nc, !detachedMode) { - zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") - } - } - var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -76,7 +70,7 @@ func attachNodeRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/authoriseNode.go b/cmd/authoriseNode.go index 3e8a9b1..f307061 100644 --- a/cmd/authoriseNode.go +++ b/cmd/authoriseNode.go @@ -38,17 +38,11 @@ func authNodeRun(cmd *cobra.Command, args []string) { detachedMode := cmd.Flags().Changed("no-prompt") - if cmdexec.CheckRemote(nc) { - if !config.ValidateNodeConfig(nc, !detachedMode) { - zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") - } - } - var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -56,7 +50,7 @@ func authNodeRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/bootstrap.go b/cmd/bootstrap.go index 56955c9..9c2c57e 100644 --- a/cmd/bootstrap.go +++ b/cmd/bootstrap.go @@ -14,7 +14,6 @@ import ( "github.com/platform9/pf9ctl/pkg/color" "github.com/platform9/pf9ctl/pkg/config" "github.com/platform9/pf9ctl/pkg/log" - "github.com/platform9/pf9ctl/pkg/objects" "github.com/platform9/pf9ctl/pkg/pmk" "github.com/platform9/pf9ctl/pkg/qbert" "github.com/platform9/pf9ctl/pkg/supportBundle" @@ -100,8 +99,8 @@ var bootstrapCmd = &cobra.Command{ }, PreRun: func(cmd *cobra.Command, args []string) { - if node.Hostname != "" { - nc.Spec.Nodes = append(nc.Spec.Nodes, node) + if util.Node.Hostname != "" { + nc.Spec.Nodes = append(nc.Spec.Nodes, util.Node) } }, @@ -109,7 +108,7 @@ var bootstrapCmd = &cobra.Command{ Run: bootstrapCmdRun, } -var node objects.Node +//var node objects.Node func init() { bootstrapCmd.Flags().IntVar(&networkStack, "network-stack", 0, "0 for ipv4 and 1 for ipv6") @@ -142,10 +141,10 @@ func init() { bootstrapCmd.Flags().BoolVar(&privileged, "privileged", true, "Enable privileged mode for K8s API, use either --privileged or --privileged=false to change") bootstrapCmd.Flags().BoolVar(&allowWorkloadsOnMaster, "allow-workloads-on-master", true, "Taint master nodes ( to enable workloads ), use either --allow-workloads-on-master or --allow-workloads-on-master=false to change") bootstrapCmd.Flags().StringVar(&networkPlugin, "network-plugin", "calico", "Specify network plugin ( Possible values: flannel or calico )") - bootstrapCmd.Flags().StringVarP(&node.Hostname, "user", "u", "", "Ssh username for the node") + bootstrapCmd.Flags().StringVarP(&util.Node.Hostname, "user", "u", "", "Ssh username for the node") bootstrapCmd.Flags().StringVarP(&nc.Password, "password", "p", "", "Ssh password for the node (use 'single quotes' to pass password)") bootstrapCmd.Flags().StringVarP(&nc.SshKey, "ssh-key", "s", "", "Ssh key file for connecting to the node") - bootstrapCmd.Flags().StringVarP(&node.Ip, "ip", "i", "", "IP address of the host to be prepared") + bootstrapCmd.Flags().StringVarP(&util.Node.Ip, "ip", "i", "", "IP address of the host to be prepared") bootstrapCmd.Flags().StringVar(&util.MFA, "mfa", "", "MFA token") bootstrapCmd.Flags().StringVarP(&util.SudoPassword, "sudo-pass", "e", "", "Sudo password for user on remote host") bootstrapCmd.Flags().BoolVarP(&util.RemoveExistingPkgs, "remove-existing-pkgs", "r", false, "Will remove previous installation if found (default false)") @@ -207,7 +206,7 @@ func bootstrapCmdRun(cmd *cobra.Command, args []string) { } detachedMode := cmd.Flags().Changed("no-prompt") - isRemote := cmdexec.CheckRemote(nc) + isRemote := cmdexec.CheckRemote(util.Node) isEtcdBackupDisabled := cmd.Flags().Changed("etcd-backup") qbert.IsMonitoringDisabled = cmd.Flags().Changed("monitoring") @@ -229,16 +228,16 @@ func bootstrapCmdRun(cmd *cobra.Command, args []string) { } if isRemote { - if !config.ValidateNodeConfig(nc, !detachedMode) { + if !config.ValidateNodeConfig(util.Node, nc, !detachedMode) { zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") } } var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -247,7 +246,7 @@ func bootstrapCmdRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/checkNode.go b/cmd/checkNode.go index 1d1395f..4458de2 100644 --- a/cmd/checkNode.go +++ b/cmd/checkNode.go @@ -26,18 +26,18 @@ var ( at https://platform9.com/blog/support/managed-container-cloud-requirements-checklist/`, Run: checkNodeRun, PreRun: func(cmd *cobra.Command, args []string) { - if node.Hostname != "" { - nc.Spec.Nodes = append(nc.Spec.Nodes, node) + if util.Node.Hostname != "" { + nc.Spec.Nodes = append(nc.Spec.Nodes, util.Node) } }, } ) func init() { - checkNodeCmd.Flags().StringVarP(&node.Hostname, "user", "u", "", "ssh username for the nodes") + checkNodeCmd.Flags().StringVarP(&util.Node.Hostname, "user", "u", "", "ssh username for the nodes") checkNodeCmd.Flags().StringVarP(&nc.Password, "password", "p", "", "ssh password for the nodes (use 'single quotes' to pass password)") checkNodeCmd.Flags().StringVarP(&nc.SshKey, "ssh-key", "s", "", "ssh key file for connecting to the nodes") - checkNodeCmd.Flags().StringVarP(&node.Ip, "ip", "i", "", "IP address of host to be prepared") + checkNodeCmd.Flags().StringVarP(&util.Node.Ip, "ip", "i", "", "IP address of host to be prepared") checkNodeCmd.Flags().StringVar(&util.MFA, "mfa", "", "MFA token") checkNodeCmd.Flags().StringVarP(&util.SudoPassword, "sudo-pass", "e", "", "sudo password for user on remote host") checkNodeCmd.Flags().BoolVarP(&util.RemoveExistingPkgs, "remove-existing-pkgs", "r", false, "Will remove previous installation if found (default false)") @@ -61,10 +61,10 @@ func checkNodeRun(cmd *cobra.Command, args []string) { } detachedMode := cmd.Flags().Changed("no-prompt") - isRemote := cmdexec.CheckRemote(nc) + isRemote := cmdexec.CheckRemote(util.Node) if isRemote { - if !config.ValidateNodeConfig(nc, !detachedMode) { + if !config.ValidateNodeConfig(util.Node, nc, !detachedMode) { zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") } } @@ -72,9 +72,9 @@ func checkNodeRun(cmd *cobra.Command, args []string) { var err error if detachedMode { util.RemoveExistingPkgs = true - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -83,7 +83,7 @@ func checkNodeRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/cloudProviders.go b/cmd/cloudProviders.go index 2815364..9a29930 100644 --- a/cmd/cloudProviders.go +++ b/cmd/cloudProviders.go @@ -7,7 +7,6 @@ import ( "github.com/platform9/pf9ctl/pkg/color" "github.com/platform9/pf9ctl/pkg/config" - "github.com/platform9/pf9ctl/pkg/objects" "github.com/platform9/pf9ctl/pkg/pmk" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -68,7 +67,7 @@ func checkGoogleProviderRun(cmd *cobra.Command, args []string) { os.Exit(1) } } else { - err = config.GetConfigRecursive("google.json", cfg, &objects.NodeConfig{}) + err = config.GetConfigRecursive("google.json", cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -89,7 +88,7 @@ func checkAmazonProviderRun(cmd *cobra.Command, args []string) { os.Exit(1) } } else { - err = config.GetConfigRecursive("amazon.json", cfg, &objects.NodeConfig{}) + err = config.GetConfigRecursive("amazon.json", cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -109,7 +108,7 @@ func checkAzureProviderRun(cmd *cobra.Command, args []string) { os.Exit(1) } } else { - err = config.GetConfigRecursive("azure.json", cfg, &objects.NodeConfig{}) + err = config.GetConfigRecursive("azure.json", cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n") diff --git a/cmd/clusterListCmd.go b/cmd/clusterListCmd.go index 489c6e5..86cceef 100644 --- a/cmd/clusterListCmd.go +++ b/cmd/clusterListCmd.go @@ -29,9 +29,9 @@ func listClusterCmdRun(cmd *cobra.Command, args []string) { var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -39,7 +39,7 @@ func listClusterCmdRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/config.go b/cmd/config.go index 4894e36..4022f5a 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -75,6 +75,7 @@ var ( } nc = &objects.NodeConfig{} + //host = objects.Node{} ) func init() { @@ -100,7 +101,7 @@ func configCmdCreateRun(cmd *cobra.Command, args []string) { } if cmd.Flags().Changed("no-prompt") { - if err = config.ValidateUserCredentials(cfg, &objects.NodeConfig{}); err != nil { + if err = config.ValidateUserCredentials(cfg); err != nil { zap.S().Fatal(color.Red("x "), err) } @@ -109,7 +110,7 @@ func configCmdCreateRun(cmd *cobra.Command, args []string) { } } else { - if err = config.GetConfigRecursive(util.Pf9DBLoc, cfg, &objects.NodeConfig{}); err != nil { + if err = config.GetConfigRecursive(util.Pf9DBLoc, cfg); err != nil { zap.S().Fatal(color.Red("x "), err) } } diff --git a/cmd/deauthoriseNode.go b/cmd/deauthoriseNode.go index 81c028b..06ab3fa 100644 --- a/cmd/deauthoriseNode.go +++ b/cmd/deauthoriseNode.go @@ -37,17 +37,11 @@ func deauthNodeRun(cmd *cobra.Command, args []string) { detachedMode := cmd.Flags().Changed("no-prompt") - if cmdexec.CheckRemote(nc) { - if !config.ValidateNodeConfig(nc, !detachedMode) { - zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") - } - } - var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -55,7 +49,7 @@ func deauthNodeRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/decomissionNode.go b/cmd/decomissionNode.go index 8ef3edc..6b93419 100644 --- a/cmd/decomissionNode.go +++ b/cmd/decomissionNode.go @@ -25,18 +25,18 @@ var decommissionNodeCmd = &cobra.Command{ }, Run: decommissionNodeRun, PreRun: func(cmd *cobra.Command, args []string) { - if node.Hostname != "" { - nc.Spec.Nodes = append(nc.Spec.Nodes, node) + if util.Node.Hostname != "" { + nc.Spec.Nodes = append(nc.Spec.Nodes, util.Node) } }, } func init() { decommissionNodeCmd.Flags().StringVar(&util.MFA, "mfa", "", "MFA token") - decommissionNodeCmd.Flags().StringVarP(&node.Hostname, "user", "u", "", "ssh username for the nodes") + decommissionNodeCmd.Flags().StringVarP(&util.Node.Hostname, "user", "u", "", "ssh username for the nodes") decommissionNodeCmd.Flags().StringVarP(&nc.Password, "password", "p", "", "ssh password for the nodes (use 'single quotes' to pass password)") decommissionNodeCmd.Flags().StringVarP(&nc.SshKey, "ssh-key", "s", "", "ssh key file for connecting to the nodes") - decommissionNodeCmd.Flags().StringVarP(&node.Ip, "ip", "i", "", "IP address of host to be decommissioned") + decommissionNodeCmd.Flags().StringVarP(&util.Node.Ip, "ip", "i", "", "IP address of host to be decommissioned") decommissionNodeCmd.Flags().StringVar(&ConfigPath, "user-config", "", "Path of user-config file") decommissionNodeCmd.Flags().StringVar(&NodeConfigPath, "node-config", "", "Path of node-config file") nodeCmd.AddCommand(decommissionNodeCmd) @@ -50,21 +50,22 @@ func decommissionNodeRun(cmd *cobra.Command, args []string) { if cmd.Flags().Changed("node-config") { config.LoadNodeConfig(nc, NodeConfigPath) + util.Node = nc.Spec.Nodes[0] } detachedMode := cmd.Flags().Changed("no-prompt") - if cmdexec.CheckRemote(nc) { - if !config.ValidateNodeConfig(nc, !detachedMode) { + if cmdexec.CheckRemote(util.Node) { + if !config.ValidateNodeConfig(util.Node, nc, !detachedMode) { zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") } } var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) diff --git a/cmd/deleteCluster.go b/cmd/deleteCluster.go index 0df0e30..5546027 100644 --- a/cmd/deleteCluster.go +++ b/cmd/deleteCluster.go @@ -47,17 +47,11 @@ func deleteClusterRun(cmd *cobra.Command, args []string) { detachedMode := cmd.Flags().Changed("no-prompt") - if cmdexec.CheckRemote(nc) { - if !config.ValidateNodeConfig(nc, !detachedMode) { - zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") - } - } - var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -65,7 +59,7 @@ func deleteClusterRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/detachNode.go b/cmd/detachNode.go index 91eb3e9..ae656b4 100644 --- a/cmd/detachNode.go +++ b/cmd/detachNode.go @@ -42,17 +42,11 @@ func detachNodeRun(cmd *cobra.Command, args []string) { detachedMode := cmd.Flags().Changed("no-prompt") - if cmdexec.CheckRemote(nc) { - if !config.ValidateNodeConfig(nc, !detachedMode) { - zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") - } - } - var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -60,7 +54,7 @@ func detachNodeRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/cmd/prepNode.go b/cmd/prepNode.go index 350db8c..8d1ae5c 100644 --- a/cmd/prepNode.go +++ b/cmd/prepNode.go @@ -8,12 +8,14 @@ import ( "fmt" "os" "strings" + "sync" "github.com/platform9/pf9ctl/pkg/client" "github.com/platform9/pf9ctl/pkg/cmdexec" "github.com/platform9/pf9ctl/pkg/color" "github.com/platform9/pf9ctl/pkg/config" "github.com/platform9/pf9ctl/pkg/log" + "github.com/platform9/pf9ctl/pkg/objects" "github.com/platform9/pf9ctl/pkg/pmk" "github.com/platform9/pf9ctl/pkg/ssh" "github.com/platform9/pf9ctl/pkg/supportBundle" @@ -37,8 +39,8 @@ var prepNodeCmd = &cobra.Command{ return nil }, PreRun: func(cmd *cobra.Command, args []string) { - if node.Hostname != "" { - nc.Spec.Nodes = append(nc.Spec.Nodes, node) + if util.Node.Hostname != "" { + nc.Spec.Nodes = append(nc.Spec.Nodes, util.Node) } }, } @@ -52,22 +54,26 @@ var ( skipChecks bool disableSwapOff bool NodeConfigPath string + wg sync.WaitGroup + isRemote bool + detachedMode bool ) func init() { - prepNodeCmd.Flags().StringVarP(&node.Hostname, "user", "u", "", "ssh username for the nodes") + prepNodeCmd.Flags().StringVarP(&util.Node.Hostname, "user", "u", "", "ssh username for the nodes") prepNodeCmd.Flags().StringVarP(&nc.Password, "password", "p", "", "ssh password for the nodes (use 'single quotes' to pass password)") prepNodeCmd.Flags().StringVarP(&nc.SshKey, "ssh-key", "s", "", "ssh key file for connecting to the nodes") - prepNodeCmd.Flags().StringVarP(&node.Ip, "ip", "i", "", "IP address of host to be prepared") + prepNodeCmd.Flags().StringVarP(&util.Node.Ip, "ip", "i", "", "IP address of host to be prepared") prepNodeCmd.Flags().BoolVarP(&skipChecks, "skip-checks", "c", false, "Will skip optional checks if true") prepNodeCmd.Flags().BoolVarP(&disableSwapOff, "disable-swapoff", "d", false, "Will skip swapoff") prepNodeCmd.Flags().StringVar(&util.MFA, "mfa", "", "MFA token") - prepNodeCmd.Flags().StringVar(&ConfigPath, "user-config", "", "Path of user-config file") - prepNodeCmd.Flags().StringVar(&NodeConfigPath, "node-config", "", "Path of node-config file") prepNodeCmd.Flags().MarkHidden("disable-swapoff") prepNodeCmd.Flags().StringVarP(&util.SudoPassword, "sudo-pass", "e", "", "sudo password for user on remote host") prepNodeCmd.Flags().BoolVarP(&util.RemoveExistingPkgs, "remove-existing-pkgs", "r", false, "Will remove previous installation if found (default false)") - + prepNodeCmd.Flags().BoolVar(&util.SkipKube, "skip-kube", false, "Skip installing pf9-kube/nodelet on this host") + prepNodeCmd.Flags().MarkHidden("skip-kube") + prepNodeCmd.Flags().StringVar(&ConfigPath, "user-config", "", "Path of user-config file") + prepNodeCmd.Flags().StringVar(&NodeConfigPath, "node-config", "", "Path of node-config file") nodeCmd.AddCommand(prepNodeCmd) } @@ -80,27 +86,22 @@ func prepNodeRun(cmd *cobra.Command, args []string) { if cmd.Flags().Changed("node-config") { config.LoadNodeConfig(nc, NodeConfigPath) + skipChecks = true + util.RemoveExistingPkgs = true } if skipChecks { pmk.WarningOptionalChecks = true } - detachedMode := cmd.Flags().Changed("no-prompt") - isRemote := cmdexec.CheckRemote(nc) - - if isRemote { - if !config.ValidateNodeConfig(nc, !detachedMode) { - zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") - } - } + detachedMode = cmd.Flags().Changed("no-prompt") var err error if detachedMode { util.RemoveExistingPkgs = true - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { @@ -109,8 +110,67 @@ func prepNodeRun(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") + + wg.Add(len(nc.Spec.Nodes)) + for _, util.Node = range nc.Spec.Nodes { + go onboardNodes(util.Node) + } + wg.Wait() + + zap.S().Debug("==========Finished running prep-node==========") +} + +// To check if Remote Host needs Password to access Sudo and prompt for Sudo Password if exists. +func SudoPasswordCheck(exec cmdexec.Executor, detached bool, sudoPass string) error { + + ssh.SudoPassword = sudoPass + + _, err := exec.RunWithStdout("-l | grep '(ALL) PASSWD: ALL'") + if err == nil { + if detached { + if sudoPass == "" { + return errors.New("sudo password is required for the user on remote host, use --sudo-pass(-e) flag to pass") + } else if validateSudoPassword(exec) == util.Invalid { + return errors.New("Invalid password for user on remote host") + } + } + + // To bail out if Sudo Password entered is invalid multiple times. + loopcounter := 1 + for true { + if loopcounter >= 4 { + zap.S().Fatalf("\n" + color.Red("x ") + "Invalid Sudo Password entered multiple times") + } + // Validate Sudo Password entered. + if ssh.SudoPassword == "" || validateSudoPassword(exec) == util.Invalid { + loopcounter += 1 + fmt.Printf("\n" + color.Red("x ") + "Invalid Sudo Password provided of Remote Host\n") + fmt.Printf("Enter Sudo password for Remote Host: ") + sudopassword, _ := terminal.ReadPassword(0) + ssh.SudoPassword = string(sudopassword) + } else { + return nil + } + } + } + return nil +} + +func validateSudoPassword(exec cmdexec.Executor) string { + + _ = pmk.CheckSudo(exec) + // Validate Sudo Password entered for Remote Host from stderr. + if strings.Contains(cmdexec.StdErrSudoPassword, util.InvalidPassword) { + return util.Invalid + } + return util.Valid +} + +func onboardNodes(node objects.Node) { + defer wg.Done() var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + var err error + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } @@ -138,6 +198,15 @@ func prepNodeRun(cmd *cobra.Command, args []string) { } zap.S().Fatalf("Unable to obtain keystone credentials: %s", err.Error()) } + + isRemote = cmdexec.CheckRemote(node) + + if isRemote { + if !config.ValidateNodeConfig(node, nc, !detachedMode) { + zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") + } + } + if isRemote { if err := SudoPasswordCheck(executor, detachedMode, util.SudoPassword); err != nil { zap.S().Fatal("Failed executing commands on remote machine with sudo: ", err.Error()) @@ -190,52 +259,4 @@ func prepNodeRun(cmd *cobra.Command, args []string) { zap.S().Debugf("Unable to prep node: %s\n", err.Error()) zap.S().Fatalf("\nFailed to prepare node. See %s or use --verbose for logs\n", log.GetLogLocation(util.Pf9Log)) } - - zap.S().Debug("==========Finished running prep-node==========") -} - -// To check if Remote Host needs Password to access Sudo and prompt for Sudo Password if exists. -func SudoPasswordCheck(exec cmdexec.Executor, detached bool, sudoPass string) error { - - ssh.SudoPassword = sudoPass - - _, err := exec.RunWithStdout("-l | grep '(ALL) PASSWD: ALL'") - if err == nil { - if detached { - if sudoPass == "" { - return errors.New("sudo password is required for the user on remote host, use --sudo-pass(-e) flag to pass") - } else if validateSudoPassword(exec) == util.Invalid { - return errors.New("Invalid password for user on remote host") - } - } - - // To bail out if Sudo Password entered is invalid multiple times. - loopcounter := 1 - for true { - if loopcounter >= 4 { - zap.S().Fatalf("\n" + color.Red("x ") + "Invalid Sudo Password entered multiple times") - } - // Validate Sudo Password entered. - if ssh.SudoPassword == "" || validateSudoPassword(exec) == util.Invalid { - loopcounter += 1 - fmt.Printf("\n" + color.Red("x ") + "Invalid Sudo Password provided of Remote Host\n") - fmt.Printf("Enter Sudo password for Remote Host: ") - sudopassword, _ := terminal.ReadPassword(0) - ssh.SudoPassword = string(sudopassword) - } else { - return nil - } - } - } - return nil -} - -func validateSudoPassword(exec cmdexec.Executor) string { - - _ = pmk.CheckSudo(exec) - // Validate Sudo Password entered for Remote Host from stderr. - if strings.Contains(cmdexec.StdErrSudoPassword, util.InvalidPassword) { - return util.Invalid - } - return util.Valid } diff --git a/cmd/supportBundle.go b/cmd/supportBundle.go index 2c507a7..cfc0f25 100644 --- a/cmd/supportBundle.go +++ b/cmd/supportBundle.go @@ -23,8 +23,8 @@ var ( Long: `Gathers support bundle that includes logs for pf9 services and pf9ctl, uploads to S3 `, Run: supportBundleUpload, PreRun: func(cmd *cobra.Command, args []string) { - if node.Hostname != "" { - nc.Spec.Nodes = append(nc.Spec.Nodes, node) + if util.Node.Hostname != "" { + nc.Spec.Nodes = append(nc.Spec.Nodes, util.Node) } }, } @@ -32,10 +32,10 @@ var ( //This initialization is using create commands which is not in use for now. func init() { - supportBundleCmd.Flags().StringVarP(&node.Hostname, "user", "u", "", "ssh username for the nodes") + supportBundleCmd.Flags().StringVarP(&util.Node.Hostname, "user", "u", "", "ssh username for the nodes") supportBundleCmd.Flags().StringVarP(&nc.Password, "password", "p", "", "ssh password for the nodes (use 'single quotes' to pass password)") supportBundleCmd.Flags().StringVarP(&nc.SshKey, "ssh-key", "s", "", "ssh key file for connecting to the nodes") - supportBundleCmd.Flags().StringVarP(&node.Ip, "ip", "i", "", "IP address of host to be prepared") + supportBundleCmd.Flags().StringVarP(&util.Node.Ip, "ip", "i", "", "IP address of host to be prepared") supportBundleCmd.Flags().StringVar(&util.MFA, "mfa", "", "MFA token") supportBundleCmd.Flags().StringVarP(&util.SudoPassword, "sudo-pass", "e", "", "sudo password for user on remote host") supportBundleCmd.Flags().StringVar(&ConfigPath, "user-config", "", "Path of user-config file") @@ -55,19 +55,19 @@ func supportBundleUpload(cmd *cobra.Command, args []string) { } detachedMode := cmd.Flags().Changed("no-prompt") - isRemote := cmdexec.CheckRemote(nc) + isRemote := cmdexec.CheckRemote(util.Node) if isRemote { - if !config.ValidateNodeConfig(nc, !detachedMode) { + if !config.ValidateNodeConfig(util.Node, nc, !detachedMode) { zap.S().Fatal("Invalid remote node config (Username/Password/IP), use 'single quotes' to pass password") } } var err error if detachedMode { - err = config.LoadConfig(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfig(util.Pf9DBLoc, cfg) } else { - err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg, nc) + err = config.LoadConfigInteractive(util.Pf9DBLoc, cfg) } if err != nil { zap.S().Fatalf("Unable to load the context: %s\n", err.Error()) @@ -75,7 +75,7 @@ func supportBundleUpload(cmd *cobra.Command, args []string) { fmt.Println(color.Green("✓ ") + "Loaded Config Successfully") zap.S().Debug("Loaded Config Successfully") var executor cmdexec.Executor - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } diff --git a/pkg/cmdexec/executor.go b/pkg/cmdexec/executor.go index d1752aa..a7c756d 100644 --- a/pkg/cmdexec/executor.go +++ b/pkg/cmdexec/executor.go @@ -163,8 +163,8 @@ func ConfidentialInfoRemover(cmd string) string { return cmd } -func GetExecutor(proxyURL string, nc *objects.NodeConfig) (Executor, error) { - if CheckRemote(nc) { +func GetExecutor(proxyURL string, node objects.Node, nc *objects.NodeConfig) (Executor, error) { + if CheckRemote(node) { var pKey []byte var err error if nc.SshKey != "" { @@ -173,14 +173,14 @@ func GetExecutor(proxyURL string, nc *objects.NodeConfig) (Executor, error) { zap.S().Fatalf("Unable to read the sshKey %s, %s", nc.SshKey, err.Error()) } } - return NewRemoteExecutor(nc.Spec.Nodes[0].Ip, 22, nc.Spec.Nodes[0].Hostname, pKey, nc.Password, proxyURL) + return NewRemoteExecutor(node.Ip, 22, node.Hostname, pKey, nc.Password, proxyURL) } zap.S().Debug("Using local executor") return LocalExecutor{ProxyUrl: proxyURL}, nil } -func CheckRemote(nc *objects.NodeConfig) bool { - for _, node := range nc.Spec.Nodes { +func CheckRemote(node objects.Node) bool { + if node.Ip != "" { if node.Ip != "localhost" && node.Ip != "127.0.0.1" && node.Ip != "::1" { return true } diff --git a/pkg/cmdexec/executor_test.go b/pkg/cmdexec/executor_test.go index 8158ecb..49cfb87 100644 --- a/pkg/cmdexec/executor_test.go +++ b/pkg/cmdexec/executor_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/platform9/pf9ctl/pkg/objects" + "github.com/platform9/pf9ctl/pkg/util" "github.com/stretchr/testify/assert" ) @@ -13,7 +14,7 @@ func TestGetExecutor(t *testing.T) { localExecutor := LocalExecutor{ProxyUrl: proxyURL} // Test local executor - executor, err := GetExecutor(proxyURL, &objects.NodeConfig{}) + executor, err := GetExecutor(proxyURL, util.Node, &objects.NodeConfig{}) assert.Equal(t, nil, err) assert.Equal(t, localExecutor, executor) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 018ba8e..24b1f5a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -11,8 +11,6 @@ import ( "reflect" "strings" - "github.com/platform9/pf9ctl/pkg/client" - "github.com/platform9/pf9ctl/pkg/cmdexec" "github.com/platform9/pf9ctl/pkg/color" "github.com/platform9/pf9ctl/pkg/keystone" "github.com/platform9/pf9ctl/pkg/objects" @@ -59,7 +57,7 @@ func StoreConfig(cfg *objects.Config, loc string) error { } // LoadConfig returns the information for communication with PF9 controller. -func LoadConfig(loc string, cfg *objects.Config, nc *objects.NodeConfig) error { +func LoadConfig(loc string, cfg *objects.Config) error { zap.S().Debug("Loading configuration details. pf9ctl version: ", util.Version) @@ -99,12 +97,12 @@ func LoadConfig(loc string, cfg *objects.Config, nc *objects.NodeConfig) error { return err } - return ValidateUserCredentials(cfg, nc) + return ValidateUserCredentials(cfg) } -func LoadConfigInteractive(loc string, cfg *objects.Config, nc *objects.NodeConfig) error { +func LoadConfigInteractive(loc string, cfg *objects.Config) error { - err := LoadConfig(loc, cfg, nc) + err := LoadConfig(loc, cfg) if err == nil { return nil } @@ -118,11 +116,11 @@ func LoadConfigInteractive(loc string, cfg *objects.Config, nc *objects.NodeConf } clearContext(cfg) - return GetConfigRecursive(loc, cfg, nc) + return GetConfigRecursive(loc, cfg) } -func GetConfigRecursive(loc string, cfg *objects.Config, nc *objects.NodeConfig) error { +func GetConfigRecursive(loc string, cfg *objects.Config) error { maxLoopNoConfig := 3 InvalidExistingConfig := false count := 0 @@ -153,7 +151,7 @@ func GetConfigRecursive(loc string, cfg *objects.Config, nc *objects.NodeConfig) continue } - if err = ValidateUserCredentials(cfg, nc); err != nil { + if err = ValidateUserCredentials(cfg); err != nil { clearContext(cfg) InvalidExistingConfig = true count++ @@ -169,19 +167,14 @@ func GetConfigRecursive(loc string, cfg *objects.Config, nc *objects.NodeConfig) return err } -func ValidateUserCredentials(cfg *objects.Config, nc *objects.NodeConfig) error { +func ValidateUserCredentials(cfg *objects.Config) error { if err := validateConfigFields(cfg); err != nil { return err } - c, err := createClient(cfg, nc) - defer c.Segment.Close() - if err != nil { - return fmt.Errorf("Error validating credentials %w", err) - } - - auth, err := c.Keystone.GetAuth( + k := keystone.NewKeystone(cfg.Spec.AccountUrl) + auth, err := k.GetAuth( cfg.Spec.Username, cfg.Spec.Password, cfg.Spec.Tenant, @@ -366,35 +359,24 @@ func ConfigCmdCreateRun(cfg *objects.Config) error { return SetProxy(cfg.Spec.ProxyURL) } -func createClient(cfg *objects.Config, nc *objects.NodeConfig) (client.Client, error) { - executor, err := cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc) - if err != nil { - //debug first since Fatalf calls os.Exit - zap.S().Debug("Error connecting to host %s", err.Error()) - zap.S().Fatalf(" Invalid (Username/Password/IP), use 'single quotes' to pass password") - } - - return client.NewClient(cfg.Spec.AccountUrl, executor, cfg.Spec.OtherData.AllowInsecure, false) -} - //This function clears the context if it is invalid. Before storing it. func clearContext(v interface{}) { p := reflect.ValueOf(v).Elem() p.Set(reflect.Zero(p.Type())) } -func ValidateNodeConfig(nc *objects.NodeConfig, interactive bool) bool { +func ValidateNodeConfig(node objects.Node, nc *objects.NodeConfig, interactive bool) bool { - if nc.Spec.Nodes[0].Hostname == "" || (nc.SshKey == "" && nc.Password == "") { + if node.Hostname == "" || (nc.SshKey == "" && nc.Password == "") { if !interactive { return false } - if nc.Spec.Nodes[0].Hostname == "" { + if node.Hostname == "" { fmt.Printf("Enter username for remote host: ") reader := bufio.NewReader(os.Stdin) - nc.Spec.Nodes[0].Hostname, _ = reader.ReadString('\n') - nc.Spec.Nodes[0].Hostname = strings.TrimSpace(nc.Spec.Nodes[0].Hostname) + node.Hostname, _ = reader.ReadString('\n') + node.Hostname = strings.TrimSpace(node.Hostname) } if nc.SshKey == "" && nc.Password == "" { var choice int diff --git a/pkg/platform/centos/centos.go b/pkg/platform/centos/centos.go index a2a8a93..f0b2fea 100644 --- a/pkg/platform/centos/centos.go +++ b/pkg/platform/centos/centos.go @@ -282,14 +282,27 @@ func (c *CentOS) Version() (string, error) { //using cat command content of os-release file is printed on terminal //using grep command os name and version are searched. e.g (CentOS Linux release 7.6.1810 (Core)) //using cut command required field (7.6.1810) is selected. - out, err := c.exec.RunWithStdout( - "bash", - "-c", - "cat /etc/*release | grep 'Red Hat Enterprise Linux Server release' -m 1 | cut -f7 -d ' ' && cat /etc/*release | grep 'CentOS Linux release' -m 1 | cut -f4 -d ' '") + var cmd string + var centos bool + _, err := c.exec.RunWithStdout("bash", "-c", "grep -i 'centos' /etc/*release") + if err != nil { + cmd = fmt.Sprintf("grep -oP '(?<=^VERSION_ID=).+' /etc/os-release") + } else { + centos = true + cmd = fmt.Sprintf("cat /etc/*release | grep 'CentOS Linux release' -m 1 | cut -f4 -d ' '") + } + + out, err := c.exec.RunWithStdout("bash", "-c", cmd) if err != nil { return "", fmt.Errorf("Couldn't read the OS configuration file os-release: %s", err.Error()) } - if match, _ := regexp.MatchString(`.*7\.[3-9]\.*`, string(out)); match { + if centos { + //comparing because we are not supporting centos 8.5 and 8.6 + if match, _ := regexp.MatchString(`.*7\.[3-9]\.*`, string(out)); match { + return "redhat", nil + } + } + if match, _ := regexp.MatchString(`.*7\.[3-9]\.*|.*8\.[5-6]\.*`, string(out)); match { return "redhat", nil } return "", fmt.Errorf("Unable to determine OS type: %s", string(out)) diff --git a/pkg/platform/debian/debian.go b/pkg/platform/debian/debian.go index b81c24c..bb068f4 100644 --- a/pkg/platform/debian/debian.go +++ b/pkg/platform/debian/debian.go @@ -297,9 +297,7 @@ func (d *Debian) Version() (string, error) { return "", fmt.Errorf("Couldn't read the OS configuration file os-release: %s", err.Error()) } var isVersionMatch bool - if strings.Contains(string(majorVersion), "16") && strings.Contains(string(minorVersion), "04") { - isVersionMatch = true - } else if strings.Contains(string(majorVersion), "18") && strings.Contains(string(minorVersion), "04") { + if strings.Contains(string(majorVersion), "18") && strings.Contains(string(minorVersion), "04") { isVersionMatch = true } else if strings.Contains(string(majorVersion), "20") && strings.Contains(string(minorVersion), "04") { isVersionMatch = true @@ -469,31 +467,15 @@ func split(version, delimiter string) (string, string, string) { func (d *Debian) IsPresent(service string) error { zap.S().Debugf("checking if %s is present", service) - var cmd string - majorVersion, minorVersion, _, err1 := d.getVersion() - if err1 != nil { - zap.S().Debugf("Couldn't read the OS configuration file os-release: %s", err1.Error()) - } - if strings.Contains(string(majorVersion), "16") && strings.Contains(string(minorVersion), "04") { - cmd = fmt.Sprintf(`systemctl status %s | grep 'not-found'`, service) - _, err := d.exec.RunWithStdout("bash", "-c", cmd) - if err != nil { - zap.S().Debugf("%s is present", service) - return nil - } else { - zap.S().Debugf("%s is not present", service) - return fmt.Errorf("%s is not present", service) - } + + cmd := fmt.Sprintf(`systemctl list-unit-files %s | grep '%s'`, service, service) + _, err := d.exec.RunWithStdout("bash", "-c", cmd) + if err != nil { + zap.S().Debugf("%s is not present", service) + return err } else { - cmd = fmt.Sprintf(`systemctl list-unit-files %s | grep '%s'`, service, service) - _, err := d.exec.RunWithStdout("bash", "-c", cmd) - if err != nil { - zap.S().Debugf("%s is not present", service) - return err - } else { - zap.S().Debugf("%s is present", service) - return nil - } + zap.S().Debugf("%s is present", service) + return nil } } diff --git a/pkg/pmk/checkNode.go b/pkg/pmk/checkNode.go index 0e4d981..f697133 100644 --- a/pkg/pmk/checkNode.go +++ b/pkg/pmk/checkNode.go @@ -62,7 +62,7 @@ func CheckNode(ctx objects.Config, allClients client.Client, auth keystone.Keyst case "redhat": platform = centos.NewCentOS(allClients.Executor) default: - return RequiredFail, fmt.Errorf("This OS is not supported. Supported operating systems are: Ubuntu (16.04, 18.04, 20.04), CentOS 7.x & RHEL 7.x") + return RequiredFail, fmt.Errorf("This OS is not supported. Supported operating systems are: Ubuntu (18.04, 20.04), CentOS 7.[3-9], RHEL 7.[3-9] & RHEL 8.[5-6]") } if err = allClients.Segment.SendEvent("Starting CheckNode", auth, checkPass, ""); err != nil { diff --git a/pkg/pmk/decomissionNode.go b/pkg/pmk/decomissionNode.go index 12d0e32..4d9ba54 100644 --- a/pkg/pmk/decomissionNode.go +++ b/pkg/pmk/decomissionNode.go @@ -5,8 +5,10 @@ import ( "strings" "time" + "github.com/briandowns/spinner" "github.com/platform9/pf9ctl/pkg/client" "github.com/platform9/pf9ctl/pkg/cmdexec" + "github.com/platform9/pf9ctl/pkg/color" "github.com/platform9/pf9ctl/pkg/objects" "github.com/platform9/pf9ctl/pkg/qbert" "github.com/platform9/pf9ctl/pkg/util" @@ -14,20 +16,16 @@ import ( ) func removePf9Installation(c client.Client) { - fmt.Println("Removing /etc/pf9 logs") cmd := fmt.Sprintf("rm -rf %s", util.EtcDir) c.Executor.RunCommandWait(cmd) - fmt.Println("Removing /var/opt/pf9 logs") cmd = fmt.Sprintf("rm -rf %s", util.OptDir) c.Executor.RunCommandWait(cmd) - fmt.Println("Removing pf9 HOME dir") cmd = fmt.Sprintf("rm -rf $HOME/pf9") c.Executor.RunCommandWait(cmd) } func removeHostagent(c client.Client, hostOS string) { - fmt.Println("Removing pf9-hostagent (this might take a few minutes...)") var services = []string{"pf9-hostagent", "pf9-nodeletd", "pf9-kubelet"} //stop hostagent for _, service := range services { @@ -45,11 +43,8 @@ func removeHostagent(c client.Client, hostOS string) { _, err = c.Executor.RunWithStdout("bash", "-c", "sudo yum remove pf9-hostagent -y") } if err != nil { - zap.S().Debugf("Could not execute command %v", err) - } else { - fmt.Println("Removed hostagent") + zap.S().Fatalf("Could not execute command %v", err) } - fmt.Println("Removing logs...") for _, file := range util.Files { cmd := fmt.Sprintf("rm -rf %s", file) c.Executor.RunCommandWait(cmd) @@ -66,9 +61,13 @@ func DecommissionNode(cfg *objects.Config, nc *objects.NodeConfig, removePf9 boo //purge pf9-hostagent //clean up logs + s := spinner.New(spinner.CharSets[9], 100*time.Millisecond) + s.Color("red") + var executor cmdexec.Executor var err error - if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, nc); err != nil { + + if executor, err = cmdexec.GetExecutor(cfg.Spec.ProxyURL, util.Node, nc); err != nil { zap.S().Fatalf("Unable to create executor: %s\n", err.Error()) } var c client.Client @@ -110,52 +109,83 @@ func DecommissionNode(cfg *objects.Config, nc *objects.NodeConfig, removePf9 boo } if nodeInfo.ClusterName == "" { - fmt.Println("Node is not connected to any cluster") + fmt.Println(color.Green("✓ ") + "Node is not connected to any cluster") if nodeConnectedToDU { err = c.Qbert.DeauthoriseNode(hostID[0], auth.Token) if err != nil { zap.S().Fatalf("Failed to deauthorize node") } else { - fmt.Println("Deauthorized node from UI") + fmt.Println(color.Green("✓ ") + "Deauthorized node from UI") } + s.Start() + s.Suffix = " Removing pf9-hostagent (this might take a few minutes...)" + //s.FinalMSG = color.Green("✓ ") + "pf9-hostagent removed successfully" removeHostagent(c, hostOS) + s.Stop() + fmt.Println(color.Green("✓ ") + "pf9-hostagent removed successfully") + } else { //case where node is not connected to DU but hostagent is installed partially + s.Start() + s.Suffix = " Removing pf9-hostagent (this might take a few minutes...)" + //s.FinalMSG = color.Green("✓ ") + "pf9-hostagent removed successfully" removeHostagent(c, hostOS) + s.Stop() + fmt.Println(color.Green("✓ ") + "pf9-hostagent removed successfully") } //remove pf9 dir if removePf9 { removePf9Installation(c) } - fmt.Println("Node decommissioning started....This may take a few minutes....Check the latest status in UI") + + s.Start() + s.Suffix = " Node decommissioning started....This may take a few minutes....Check the latest status in UI" + s.FinalMSG = color.Green("✓ ") + "Node decommission completed\n" time.Sleep(50 * time.Second) + s.Stop() + } else { //detach node from cluster - fmt.Printf("Node is connected to %s cluster\n", nodeInfo.ClusterName) - fmt.Println("Detaching node from cluster...") + fmt.Printf(color.Green("✓ ")+"Node is connected to %s cluster\n", nodeInfo.ClusterName) + //fmt.Println("Detaching node from cluster...") + s.Start() + s.Suffix = " Detaching node from cluster..." err = c.Qbert.DetachNode(nodeInfo.ClusterUuid, auth.ProjectID, auth.Token, hostID[0]) if err != nil { + s.Stop() zap.S().Fatalf("Failed to detach host from cluster") } else { - fmt.Println("Detached node from cluster") + s.Stop() + fmt.Println(color.Green("✓ ") + "Detached node from cluster") } //deauthorize host from UI - fmt.Println("Deauthorizing node from UI...") + //fmt.Println("Deauthorizing node from UI...") + s.Restart() + s.Suffix = " Deauthorizing node from UI..." err = c.Qbert.DeauthoriseNode(hostID[0], auth.Token) if err != nil { + s.Stop() zap.S().Fatalf("Failed to deauthorize node") } else { - fmt.Println("Deauthorized node from UI") + s.Stop() + fmt.Println(color.Green("✓ ") + "Deauthorized node from UI") } //stop host agent and remove it + s.Restart() + s.Suffix = " Removing pf9-hostagent (this might take a few minutes...)" removeHostagent(c, hostOS) + s.Stop() + fmt.Println(color.Green("✓ ") + "pf9-hostagent removed successfully") //remove pf9 dir if removePf9 { removePf9Installation(c) } - fmt.Println("Node decommissioning started....This may take a few minutes....Check the latest status in UI") + s.Restart() + s.Suffix = " Node decommissioning started....This may take a few minutes....Check the latest status in UI" + s.FinalMSG = color.Green("✓ ") + "Node decommission completed\n" time.Sleep(50 * time.Second) + s.Stop() } } else { fmt.Println("Host is not connected to Platform9 Management Plane") diff --git a/pkg/pmk/node.go b/pkg/pmk/node.go index 9a07a48..b2f0c1f 100644 --- a/pkg/pmk/node.go +++ b/pkg/pmk/node.go @@ -116,16 +116,21 @@ func PrepNode(ctx objects.Config, allClients client.Client, auth keystone.Keysto return fmt.Errorf(errStr) } + hostName, err := allClients.Executor.RunWithStdout("bash", "-c", "hostname") + if err != nil { + zap.S().Debugf("error getting host name") + } + hostName = strings.TrimSpace(hostName) s.Suffix = " Platform9 packages installed successfully" if HostAgent == HostAgentCertless { s.Suffix = " Platform9 packages installed successfully" s.Stop() - fmt.Println(color.Green("✓ ") + "Platform9 packages installed successfully") + fmt.Println(color.Green("✓ ") + "Platform9 packages installed successfully" + " on host " + hostName) } else if HostAgent == HostAgentLegacy { s.Suffix = " Hostagent installed successfully" s.Stop() - fmt.Println(color.Green("✓ ") + "Hostagent installed successfully") + fmt.Println(color.Green("✓ ") + "Hostagent installed successfully" + " on host " + hostName) } s.Restart() @@ -143,15 +148,20 @@ func PrepNode(ctx objects.Config, allClients client.Client, auth keystone.Keysto } s.Stop() - fmt.Println(color.Green("✓ ") + "Initialised host successfully") - zap.S().Debug("Initialised host successfully") + fmt.Println(color.Green("✓ ") + "Initialised host " + hostName + " successfully") + zap.S().Debug("Initialised host " + hostName + " successfully") + if util.SkipKube { + zap.S().Debug("Skip authorizing host as --skip-kube flag is true") + sendSegmentEvent(allClients, "Successful", auth, false) + return nil + } + s.Restart() s.Suffix = " Authorising host" zap.S().Debug("Authorising host") hostID := strings.TrimSuffix(output, "\n") time.Sleep(ctx.Spec.OtherData.WaitPeriod * time.Second) - sendSegmentEvent(allClients, "Authorising host - 4", auth, false) if err := allClients.Resmgr.AuthorizeHost(hostID, auth.Token); err != nil { errStr := "Error: Unable to authorise host. " + err.Error() sendSegmentEvent(allClients, errStr, auth, true) @@ -163,7 +173,7 @@ func PrepNode(ctx objects.Config, allClients client.Client, auth keystone.Keysto sendSegmentEvent(allClients, "Successful", auth, false) s.Stop() - fmt.Println(color.Green("✓ ") + "Host successfully attached to the Platform9 control-plane") + fmt.Println(color.Green("✓ ") + "Host " + hostName + " successfully attached to the Platform9 control-plane") return nil } diff --git a/pkg/util/constants.go b/pkg/util/constants.go index ca6dde4..7036b65 100644 --- a/pkg/util/constants.go +++ b/pkg/util/constants.go @@ -4,15 +4,20 @@ import ( "os" "path/filepath" "time" + + "github.com/platform9/pf9ctl/pkg/objects" ) var Files []string var Pf9Packages []string var RequiredPorts []string var PortErr string -var ProcessesList []string //Kubernetes clusters processes list -var SwapOffDisabled bool //If this is true the swapOff functionality will be disabled. +var ProcessesList []string // Kubernetes clusters processes list +var SwapOffDisabled bool // If this is true the swapOff functionality will be disabled. var SkipPrepNode bool + +// SkipKube skips authorizing kube role during prep-node. Not applicable to bootstrap command +var SkipKube bool var HostDown bool var EBSPermissions []string var Route53Permissions []string @@ -29,6 +34,7 @@ var SudoPassword string var RemoveExistingPkgs bool var InstallerErrors = make(map[int]string) var LogFileNamePath string +var Node objects.Node const ( @@ -307,7 +313,7 @@ func init() { //These are the constants needed for everything version related const ( - Version string = "pf9ctl version: v1.15" + Version string = "pf9ctl version: v1.16" AWSBucketName string = "pmkft-assets" AWSBucketKey string = "pf9ctl" AWSBucketRegion string = "us-west-1"