github.com/kubeshop/testkube@v1.17.23/cmd/kubectl-testkube/commands/init.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "os/exec" 6 "strings" 7 8 "github.com/pterm/pterm" 9 "github.com/spf13/cobra" 10 11 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" 12 "github.com/kubeshop/testkube/cmd/kubectl-testkube/config" 13 "github.com/kubeshop/testkube/pkg/process" 14 "github.com/kubeshop/testkube/pkg/telemetry" 15 "github.com/kubeshop/testkube/pkg/ui" 16 ) 17 18 const ( 19 defaultNamespace = "testkube" 20 standaloneAgentProfile = "standalone-agent" 21 demoProfile = "demo" 22 agentProfile = "agent" 23 24 standaloneInstallationName = "Testkube OSS" 25 demoInstallationName = "Testkube On-Prem demo" 26 agentInstallationName = "Testkube Agent" 27 licenseFormat = "XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXXXXX-V3" 28 ) 29 30 func NewInitCmd() *cobra.Command { 31 standaloneCmd := NewInitCmdStandalone() 32 33 cmd := &cobra.Command{ 34 Use: "init <profile>", 35 Aliases: []string{"g"}, 36 Short: "Init Testkube profiles(" + standaloneAgentProfile + "|" + demoProfile + "|" + agentProfile + ")", 37 Long: "Init installs the Testkube in your cluster as follows:\n" + 38 "\t" + standaloneAgentProfile + " -> " + standaloneInstallationName + "\n" + 39 "\t" + demoProfile + " -> " + demoInstallationName + "\n" + 40 "\t" + agentProfile + " -> " + agentInstallationName, 41 Run: func(cmd *cobra.Command, args []string) { 42 standaloneCmd.Run(cmd, args) 43 }, 44 } 45 46 cmd.AddCommand(standaloneCmd) 47 cmd.AddCommand(NewInitCmdDemo()) 48 49 return cmd 50 } 51 52 func NewInitCmdStandalone() *cobra.Command { 53 var options common.HelmOptions 54 55 cmd := &cobra.Command{ 56 Use: standaloneAgentProfile, 57 Short: "Install " + standaloneInstallationName + " in your current context", 58 Aliases: []string{"install"}, 59 Run: func(cmd *cobra.Command, args []string) { 60 ui.Logo() 61 ui.Info("Welcome to the installer for " + standaloneInstallationName + ".") 62 ui.NL() 63 64 if !isContextApproved(options.NoConfirm, standaloneInstallationName) { 65 return 66 } 67 68 common.ProcessMasterFlags(cmd, &options, nil) 69 70 err := common.HelmUpgradeOrInstalTestkube(options) 71 ui.ExitOnError("Cannot install Testkube", err) 72 73 ui.Info(`To help improve the quality of Testkube, we collect anonymous basic telemetry data. Head out to https://docs.testkube.io/articles/telemetry to read our policy or feel free to:`) 74 75 ui.NL() 76 ui.ShellCommand("disable telemetry by typing", "testkube disable telemetry") 77 ui.NL() 78 79 ui.Info(" Happy Testing! 🚀") 80 ui.NL() 81 82 }, 83 } 84 85 common.PopulateHelmFlags(cmd, &options) 86 common.PopulateMasterFlags(cmd, &options) 87 88 return cmd 89 } 90 91 func NewInitCmdDemo() *cobra.Command { 92 var noConfirm, dryRun bool 93 var license, namespace string 94 95 cmd := &cobra.Command{ 96 Use: demoProfile, 97 Short: "Install " + demoInstallationName + " Helm chart registry in current kubectl context and update dependencies", 98 Aliases: []string{"on-premise-demo", "on-prem-demo", "enterprise-demo"}, 99 Run: func(cmd *cobra.Command, args []string) { 100 ui.Logo() 101 ui.Info("Welcome to the installer for " + demoInstallationName + ".") 102 ui.NL() 103 104 cfg, err := config.Load() 105 ui.ExitOnError("loading config file", err) 106 107 kubecontext, err := common.GetCurrentKubernetesContext() 108 if err != nil { 109 ui.Failf("kubeconfig not found") 110 } 111 112 if namespace == "" { 113 if noConfirm { 114 namespace = defaultNamespace 115 } else { 116 response, err := pterm.DefaultInteractiveTextInput.WithDefaultValue("testkube").Show("Enter namespace for this installation") 117 namespace = response 118 ui.ExitOnError("cannot read namespace", err) 119 } 120 } 121 122 if license == "" { 123 response, err := pterm.DefaultInteractiveTextInput.Show("Enter license key") 124 license = strings.TrimSpace(response) 125 ui.ExitOnError("cannot read license", err) 126 } 127 128 if len(license) != len(licenseFormat) { 129 sendErrTelemetry(cmd, cfg, "install_license_malformed", license, err) 130 ui.Failf("license malformed, expected license of format: " + licenseFormat) 131 } 132 133 ui.NL() 134 ui.Warn("Installation is about to start and may take a several minutes:") 135 ui.NL() 136 ui.Warn("- Testkube will be installed in the " + kubecontext + " context.") 137 ui.Warn("- Testkube services will be applied to the " + namespace + " namespace.") 138 ui.Warn("- Testkube CRDs and cluster roles will be applied to your cluster.") 139 ui.NL() 140 141 if ok := ui.Confirm("Do you want to continue"); !ok { 142 sendErrTelemetry(cmd, cfg, "install_cancelled", license, err) 143 return 144 } 145 146 sendAttemptTelemetry(cmd, cfg, license) 147 err = helmInstallDemo(license, namespace, dryRun) 148 if err != nil { 149 sendErrTelemetry(cmd, cfg, "install_failed", license, err) 150 } 151 ui.ExitOnError("Cannot install Testkube", err) 152 153 if err == nil { 154 cfg.Namespace = namespace 155 err = config.Save(cfg) 156 if err != nil { 157 ui.Debug("Cannot save config") 158 } 159 } 160 161 ui.Info("Your initial admin credentials are: admin@example.com / password") 162 ui.Info("Make sure to copy these credentials now as you will not be able to see this again.") 163 ui.NL() 164 ok := ui.Confirm("Do you want to continue?") 165 166 ui.Info("You can use `testkube dashboard` to access Testkube without exposing services.") 167 ui.NL() 168 169 if !ok { 170 return 171 } 172 173 if ok := ui.Confirm("Do you want to open the dashboard?"); ok { 174 cfg, err := config.Load() 175 ui.ExitOnError("Cannot open dashboard", err) 176 openOnPremDashboard(nil, cfg, false) 177 } 178 }, 179 } 180 181 cmd.Flags().BoolVarP(&noConfirm, "export", "", false, "Export the values.yaml") 182 cmd.Flags().BoolVarP(&noConfirm, "no-confirm", "y", false, "Skip confirmation") 183 cmd.Flags().StringVarP(&license, "license", "l", "", "License key") 184 cmd.Flags().BoolVarP(&dryRun, "dry-run", "", false, "Dry run") 185 cmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace to install "+demoInstallationName) 186 187 return cmd 188 } 189 190 func isContextApproved(isNoConfirm bool, installedComponent string) bool { 191 if !isNoConfirm { 192 ui.Warn("This will install " + installedComponent + " to the latest version. This may take a few minutes.") 193 ui.Warn("Please be sure you're on valid kubectl context before continuing!") 194 ui.NL() 195 196 currentContext, err := common.GetCurrentKubernetesContext() 197 ui.ExitOnError("getting current context", err) 198 ui.Alert("Current kubectl context:", currentContext) 199 ui.NL() 200 201 ok := ui.Confirm("Do you want to continue?") 202 if !ok { 203 ui.Errf("Installation cancelled") 204 return false 205 } 206 } 207 return true 208 } 209 210 func helmInstallDemo(license, namespace string, dryRun bool) error { 211 helmPath, err := exec.LookPath("helm") 212 if err != nil { 213 return err 214 } 215 216 ui.Info("Installing Testkube… ") 217 218 args := []string{"repo", "add", "testkubeenterprise", "https://kubeshop.github.io/testkube-cloud-charts"} 219 _, err = process.ExecuteWithOptions(process.Options{Command: helmPath, Args: args, DryRun: dryRun}) 220 if err != nil && !strings.Contains(err.Error(), "Error: repository name (kubeshop) already exists, please specify a different name") { 221 return err 222 } 223 224 _, err = process.ExecuteWithOptions(process.Options{Command: helmPath, Args: []string{"repo", "update"}, DryRun: dryRun}) 225 if err != nil { 226 return err 227 } 228 229 args = []string{"upgrade", "--install", 230 "--create-namespace", 231 "--namespace", namespace, 232 "--set", "global.enterpriseLicenseKey=" + license, 233 "--values", "https://raw.githubusercontent.com/kubeshop/testkube-cloud-charts/main/charts/testkube-enterprise/profiles/values.demo.yaml", 234 "--wait", 235 "testkube", "testkubeenterprise/testkube-enterprise"} 236 237 ui.Debug("Helm command: ", helmPath+" "+strings.Join(args, " ")) 238 out, err := process.ExecuteWithOptions(process.Options{Command: helmPath, Args: args, DryRun: dryRun}) 239 if err != nil { 240 return err 241 } 242 ui.Debug("Helm command output: ", string(out)) 243 ui.NL() 244 return nil 245 } 246 247 func sendErrTelemetry(cmd *cobra.Command, clientCfg config.Data, errType, license string, errorLogs error) { 248 errorStackTrace := fmt.Sprintf("%+v", errorLogs) 249 if clientCfg.TelemetryEnabled { 250 out, err := telemetry.SendCmdErrorEventWithLicense(cmd, common.Version, errType, license, errorStackTrace) 251 if ui.Verbose && err != nil { 252 ui.Err(err) 253 } 254 255 ui.Debug("telemetry send event response", out) 256 } 257 } 258 259 func sendAttemptTelemetry(cmd *cobra.Command, clientCfg config.Data, license string) { 260 if clientCfg.TelemetryEnabled { 261 out, err := telemetry.SendCmdAttempWithLicenseEvent(cmd, common.Version, license) 262 if ui.Verbose && err != nil { 263 ui.Err(err) 264 } 265 ui.Debug("telemetry send event response", out) 266 } 267 }