github.com/kubeshop/testkube@v1.17.23/cmd/kubectl-testkube/commands/root.go (about)

     1  package commands
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"syscall"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/spf13/cobra"
    12  
    13  	"golang.org/x/sync/errgroup"
    14  
    15  	"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
    16  	"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/validator"
    17  	"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/pro"
    18  	"github.com/kubeshop/testkube/cmd/kubectl-testkube/config"
    19  	"github.com/kubeshop/testkube/pkg/telemetry"
    20  	"github.com/kubeshop/testkube/pkg/ui"
    21  )
    22  
    23  var (
    24  	client       string
    25  	verbose      bool
    26  	namespace    string
    27  	oauthEnabled bool
    28  	insecure     bool
    29  )
    30  
    31  func init() {
    32  	// New commands
    33  	RootCmd.AddCommand(NewCreateCmd())
    34  	RootCmd.AddCommand(NewUpdateCmd())
    35  
    36  	RootCmd.AddCommand(NewGetCmd())
    37  	RootCmd.AddCommand(NewSetCmd())
    38  	RootCmd.AddCommand(NewRunCmd())
    39  	RootCmd.AddCommand(NewDeleteCmd())
    40  	RootCmd.AddCommand(NewAbortCmd())
    41  
    42  	RootCmd.AddCommand(NewEnableCmd())
    43  	RootCmd.AddCommand(NewDisableCmd())
    44  	RootCmd.AddCommand(NewStatusCmd())
    45  
    46  	RootCmd.AddCommand(NewDownloadCmd())
    47  	RootCmd.AddCommand(NewGenerateCmd())
    48  
    49  	RootCmd.AddCommand(NewInitCmd())
    50  	RootCmd.AddCommand(NewUpgradeCmd())
    51  	RootCmd.AddCommand(NewPurgeCmd())
    52  	RootCmd.AddCommand(NewWatchCmd())
    53  	RootCmd.AddCommand(NewDashboardCmd())
    54  	RootCmd.AddCommand(NewMigrateCmd())
    55  	RootCmd.AddCommand(NewVersionCmd())
    56  
    57  	RootCmd.AddCommand(NewConfigCmd())
    58  	RootCmd.AddCommand(NewDebugCmd())
    59  	RootCmd.AddCommand(NewCreateTicketCmd())
    60  
    61  	RootCmd.AddCommand(NewAgentCmd())
    62  	RootCmd.AddCommand(NewCloudCmd())
    63  	RootCmd.AddCommand(NewProCmd())
    64  	RootCmd.AddCommand(pro.NewLoginCmd())
    65  
    66  	RootCmd.SetHelpCommand(NewHelpCmd())
    67  }
    68  
    69  var RootCmd = &cobra.Command{
    70  	Use:   "testkube",
    71  	Short: "Testkube entrypoint for kubectl plugin",
    72  	PersistentPreRun: func(cmd *cobra.Command, args []string) {
    73  		ui.SetVerbose(verbose)
    74  
    75  		// don't validate context before set and completion
    76  		if cmd.Name() == "context" || (cmd.Parent() != nil && cmd.Parent().Name() == "completion") {
    77  			return
    78  		}
    79  
    80  		cfg, err := config.Load()
    81  		ui.ExitOnError("loading config", err)
    82  
    83  		if err = validator.ValidateCloudContext(cfg); err != nil {
    84  			common.UiCloudContextValidationError(err)
    85  		}
    86  	},
    87  
    88  	PersistentPostRun: func(cmd *cobra.Command, args []string) {
    89  		clientCfg, err := config.Load()
    90  		ui.WarnOnError("loading config", err)
    91  
    92  		client, _, err := common.GetClient(cmd)
    93  		if err != nil {
    94  			return
    95  		}
    96  		// We ignore this check for cloud, since agent can be offline, and config API won't work
    97  		// but other commands should work.
    98  		if clientCfg.ContextType != config.ContextTypeCloud {
    99  			serverCfg, err := client.GetConfig()
   100  			if ui.Verbose && err != nil {
   101  				ui.Err(err)
   102  			}
   103  
   104  			if clientCfg.TelemetryEnabled != serverCfg.EnableTelemetry && err == nil {
   105  				if serverCfg.EnableTelemetry {
   106  					clientCfg.EnableAnalytics()
   107  					ui.Debug("Sync telemetry on CLI with API", "enabled")
   108  				} else {
   109  					clientCfg.DisableAnalytics()
   110  					ui.Debug("Sync telemetry on CLI with API", "disabled")
   111  				}
   112  
   113  				err = config.Save(clientCfg)
   114  				ui.WarnOnError("syncing config", err)
   115  			}
   116  		}
   117  		if clientCfg.TelemetryEnabled {
   118  			ui.Debug("collecting anonymous telemetry data, you can disable it by calling `kubectl testkube disable telemetry`")
   119  			out, err := telemetry.SendCmdEvent(cmd, common.Version)
   120  			if ui.Verbose && err != nil {
   121  				ui.Err(err)
   122  			}
   123  			ui.Debug("telemetry send event response", out)
   124  
   125  			// trigger init event only for first run
   126  			clientCfg, err := config.Load()
   127  			ui.WarnOnError("loading config", err)
   128  
   129  			if !clientCfg.Initialized {
   130  				clientCfg.SetInitialized()
   131  				err := config.Save(clientCfg)
   132  				ui.WarnOnError("saving config", err)
   133  
   134  				ui.Debug("sending 'init' event")
   135  
   136  				out, err := telemetry.SendCmdInitEvent(cmd, common.Version)
   137  				if ui.Verbose && err != nil {
   138  					ui.Err(err)
   139  				}
   140  				ui.Debug("telemetry init event response", out)
   141  			}
   142  		}
   143  	},
   144  
   145  	Run: func(cmd *cobra.Command, args []string) {
   146  		ui.Logo()
   147  		err := cmd.Usage()
   148  		ui.PrintOnError("Displaying usage", err)
   149  		cmd.DisableAutoGenTag = true
   150  	},
   151  }
   152  
   153  func Execute() {
   154  	cfg, err := config.Load()
   155  	ui.WarnOnError("loading config", err)
   156  
   157  	defaultNamespace := "testkube"
   158  	if cfg.Namespace != "" {
   159  		defaultNamespace = cfg.Namespace
   160  	}
   161  
   162  	apiURI := "http://localhost:8088"
   163  	if cfg.APIURI != "" {
   164  		apiURI = cfg.APIURI
   165  	}
   166  
   167  	if os.Getenv("TESTKUBE_API_URI") != "" {
   168  		apiURI = os.Getenv("TESTKUBE_API_URI")
   169  	}
   170  
   171  	// Run services within an errgroup to propagate errors between services.
   172  	g, ctx := errgroup.WithContext(context.Background())
   173  
   174  	// Cancel the errgroup context on SIGINT and SIGTERM,
   175  	// which shuts everything down gracefully.
   176  	stopSignal := make(chan os.Signal, 1)
   177  	signal.Notify(stopSignal, syscall.SIGINT, syscall.SIGTERM)
   178  	g.Go(func() error {
   179  		select {
   180  		case <-ctx.Done():
   181  			return nil
   182  		case sig := <-stopSignal:
   183  			go func() {
   184  				<-stopSignal
   185  				os.Exit(137)
   186  			}()
   187  			return errors.Errorf("received signal: %v", sig)
   188  		}
   189  	})
   190  
   191  	RootCmd.PersistentFlags().StringVarP(&client, "client", "c", "proxy", "client used for connecting to Testkube API one of proxy|direct|cluster")
   192  	RootCmd.PersistentFlags().StringVarP(&namespace, "namespace", "", defaultNamespace, "Kubernetes namespace, default value read from config if set")
   193  	RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "", false, "show additional debug messages")
   194  	RootCmd.PersistentFlags().StringVarP(&apiURI, "api-uri", "a", apiURI, "api uri, default value read from config if set")
   195  	RootCmd.PersistentFlags().BoolVarP(&oauthEnabled, "oauth-enabled", "", cfg.OAuth2Data.Enabled, "enable oauth")
   196  	RootCmd.PersistentFlags().BoolVarP(&insecure, "insecure", "", false, "insecure connection for direct client")
   197  
   198  	if err := RootCmd.ExecuteContext(ctx); err != nil {
   199  		fmt.Fprintln(os.Stderr, err)
   200  		os.Exit(1)
   201  	}
   202  }