-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: Create structs for each command to better isolate commands #3322
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,100 +16,121 @@ import ( | |
"github.com/zarf-dev/zarf/src/pkg/utils/exec" | ||
) | ||
|
||
var ( | ||
// ConnectOptions holds the command-line options for 'connect' sub-command. | ||
type ConnectOptions struct { | ||
cliOnly bool | ||
zt cluster.TunnelInfo | ||
) | ||
var connectCmd = &cobra.Command{ | ||
Use: "connect { REGISTRY | GIT | connect-name }", | ||
Aliases: []string{"c"}, | ||
Short: lang.CmdConnectShort, | ||
Long: lang.CmdConnectLong, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctx := cmd.Context() | ||
l := logger.From(ctx) | ||
target := "" | ||
if len(args) > 0 { | ||
target = args[0] | ||
} | ||
|
||
spinner := message.NewProgressSpinner(lang.CmdConnectPreparingTunnel, target) | ||
defer spinner.Stop() | ||
} | ||
|
||
c, err := cluster.NewCluster() | ||
if err != nil { | ||
return err | ||
} | ||
// NewConnectCommand creates the `connect` sub-command and its nested children. | ||
func NewConnectCommand() *cobra.Command { | ||
o := &ConnectOptions{} | ||
|
||
var tunnel *cluster.Tunnel | ||
if target == "" { | ||
tunnel, err = c.ConnectTunnelInfo(ctx, zt) | ||
} else { | ||
var ti cluster.TunnelInfo | ||
ti, err = c.NewTargetTunnelInfo(ctx, target) | ||
if err != nil { | ||
return fmt.Errorf("unable to create tunnel: %w", err) | ||
} | ||
if zt.LocalPort != 0 { | ||
ti.LocalPort = zt.LocalPort | ||
} | ||
tunnel, err = c.ConnectTunnelInfo(ctx, ti) | ||
} | ||
cmd := &cobra.Command{ | ||
Use: "connect { REGISTRY | GIT | connect-name }", | ||
Aliases: []string{"c"}, | ||
Short: lang.CmdConnectShort, | ||
Long: lang.CmdConnectLong, | ||
RunE: o.Run, | ||
} | ||
|
||
if err != nil { | ||
return fmt.Errorf("unable to connect to the service: %w", err) | ||
} | ||
cmd.Flags().StringVar(&o.zt.ResourceName, "name", "", lang.CmdConnectFlagName) | ||
cmd.Flags().StringVar(&o.zt.Namespace, "namespace", cluster.ZarfNamespaceName, lang.CmdConnectFlagNamespace) | ||
cmd.Flags().StringVar(&o.zt.ResourceType, "type", cluster.SvcResource, lang.CmdConnectFlagType) | ||
cmd.Flags().IntVar(&o.zt.LocalPort, "local-port", 0, lang.CmdConnectFlagLocalPort) | ||
cmd.Flags().IntVar(&o.zt.RemotePort, "remote-port", 0, lang.CmdConnectFlagRemotePort) | ||
cmd.Flags().BoolVar(&o.cliOnly, "cli-only", false, lang.CmdConnectFlagCliOnly) | ||
|
||
defer tunnel.Close() | ||
|
||
if cliOnly { | ||
spinner.Updatef(lang.CmdConnectEstablishedCLI, tunnel.FullURL()) | ||
l.Info("Tunnel established, waiting for user to interrupt (ctrl-c to end)", "url", tunnel.FullURL()) | ||
} else { | ||
spinner.Updatef(lang.CmdConnectEstablishedWeb, tunnel.FullURL()) | ||
l.Info("Tunnel established, opening your default web browser (ctrl-c to end)", "url", tunnel.FullURL()) | ||
if err := exec.LaunchURL(tunnel.FullURL()); err != nil { | ||
return err | ||
} | ||
} | ||
// TODO(soltysh): consider splitting sub-commands into separate files | ||
cmd.AddCommand(NewConnectListCommand()) | ||
|
||
// Wait for the interrupt signal or an error. | ||
select { | ||
case <-ctx.Done(): | ||
spinner.Successf(lang.CmdConnectTunnelClosed, tunnel.FullURL()) | ||
return nil | ||
case err = <-tunnel.ErrChan(): | ||
return fmt.Errorf("lost connection to the service: %w", err) | ||
} | ||
}, | ||
return cmd | ||
} | ||
|
||
var connectListCmd = &cobra.Command{ | ||
Use: "list", | ||
Aliases: []string{"l"}, | ||
Short: lang.CmdConnectListShort, | ||
RunE: func(cmd *cobra.Command, _ []string) error { | ||
c, err := cluster.NewCluster() | ||
// Run performs the execution of 'connect' sub command. | ||
func (o *ConnectOptions) Run(cmd *cobra.Command, args []string) error { | ||
ctx := cmd.Context() | ||
l := logger.From(ctx) | ||
target := "" | ||
if len(args) > 0 { | ||
target = args[0] | ||
} | ||
|
||
spinner := message.NewProgressSpinner(lang.CmdConnectPreparingTunnel, target) | ||
defer spinner.Stop() | ||
|
||
c, err := cluster.NewCluster() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Definitely not in scope for this PR, but sometime in the future it'd be great to ensure every CLI command can call a clearly defined API fn that encapsulates all of the configuration. I don't think this intent is well captured in an issue atm, but Connect has complex logic that could be abstracted out and made me think of the discussions we've had around it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, definitely. Have a look at https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/events/events.go - that's my ideal command. There's a very strict boundary between the CLI bits ( The flow is you create I'll be working on followup steps, once we get this in, but due to the amount of changes I want to do it slowly 😅 for ease of reviews. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This all tracks - having explicit boundaries and a validation step are great. |
||
if err != nil { | ||
return err | ||
} | ||
|
||
var tunnel *cluster.Tunnel | ||
if target == "" { | ||
tunnel, err = c.ConnectTunnelInfo(ctx, o.zt) | ||
} else { | ||
var ti cluster.TunnelInfo | ||
ti, err = c.NewTargetTunnelInfo(ctx, target) | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("unable to create tunnel: %w", err) | ||
} | ||
connections, err := c.ListConnections(cmd.Context()) | ||
if err != nil { | ||
if o.zt.LocalPort != 0 { | ||
ti.LocalPort = o.zt.LocalPort | ||
} | ||
tunnel, err = c.ConnectTunnelInfo(ctx, ti) | ||
} | ||
|
||
if err != nil { | ||
return fmt.Errorf("unable to connect to the service: %w", err) | ||
} | ||
|
||
defer tunnel.Close() | ||
|
||
if o.cliOnly { | ||
spinner.Updatef(lang.CmdConnectEstablishedCLI, tunnel.FullURL()) | ||
l.Info("Tunnel established, waiting for user to interrupt (ctrl-c to end)", "url", tunnel.FullURL()) | ||
} else { | ||
spinner.Updatef(lang.CmdConnectEstablishedWeb, tunnel.FullURL()) | ||
l.Info("Tunnel established, opening your default web browser (ctrl-c to end)", "url", tunnel.FullURL()) | ||
if err := exec.LaunchURL(tunnel.FullURL()); err != nil { | ||
return err | ||
} | ||
message.PrintConnectStringTable(connections) | ||
} | ||
|
||
// Wait for the interrupt signal or an error. | ||
select { | ||
case <-ctx.Done(): | ||
spinner.Successf(lang.CmdConnectTunnelClosed, tunnel.FullURL()) | ||
return nil | ||
}, | ||
case err = <-tunnel.ErrChan(): | ||
return fmt.Errorf("lost connection to the service: %w", err) | ||
} | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(connectCmd) | ||
connectCmd.AddCommand(connectListCmd) | ||
// ConnectOptions holds the command-line options for 'connect list' sub-command. | ||
type ConnectListOptions struct{} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 good pattern |
||
|
||
// NewConnectListCommand creates the `connect list` sub-command. | ||
func NewConnectListCommand() *cobra.Command { | ||
o := &ConnectListOptions{} | ||
cmd := &cobra.Command{ | ||
Use: "list", | ||
Aliases: []string{"l"}, | ||
Short: lang.CmdConnectListShort, | ||
RunE: o.Run, | ||
} | ||
return cmd | ||
} | ||
|
||
connectCmd.Flags().StringVar(&zt.ResourceName, "name", "", lang.CmdConnectFlagName) | ||
connectCmd.Flags().StringVar(&zt.Namespace, "namespace", cluster.ZarfNamespaceName, lang.CmdConnectFlagNamespace) | ||
connectCmd.Flags().StringVar(&zt.ResourceType, "type", cluster.SvcResource, lang.CmdConnectFlagType) | ||
connectCmd.Flags().IntVar(&zt.LocalPort, "local-port", 0, lang.CmdConnectFlagLocalPort) | ||
connectCmd.Flags().IntVar(&zt.RemotePort, "remote-port", 0, lang.CmdConnectFlagRemotePort) | ||
connectCmd.Flags().BoolVar(&cliOnly, "cli-only", false, lang.CmdConnectFlagCliOnly) | ||
// Run performs the execution of 'connect list' sub-command. | ||
func (o *ConnectListOptions) Run(cmd *cobra.Command, _ []string) error { | ||
c, err := cluster.NewCluster() | ||
if err != nil { | ||
return err | ||
} | ||
connections, err := c.ListConnections(cmd.Context()) | ||
if err != nil { | ||
return err | ||
} | ||
message.PrintConnectStringTable(connections) | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good choice to isolate these out into function declarations on the pkg