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  }