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 }