diff --git a/site/src/content/docs/commands/zarf_connect.md b/site/src/content/docs/commands/zarf_connect.md index 88f519d332..f0eb9b84ce 100644 --- a/site/src/content/docs/commands/zarf_connect.md +++ b/site/src/content/docs/commands/zarf_connect.md @@ -29,11 +29,11 @@ zarf connect { REGISTRY | GIT | connect-name } [flags] ``` --cli-only Disable browser auto-open -h, --help help for connect - --local-port int (Optional, autogenerated if not provided) Specify the local port to bind to. E.g. local-port=42000. Ignored if --name is unset. - --name string Specify the resource name. E.g. name=unicorns or name=unicorn-pod-7448499f4d-b5bk6. - --namespace string Specify the namespace. E.g. namespace=default. Ignored if --name is unset. (default "zarf") - --remote-port int Specify the remote port of the resource to bind to. E.g. remote-port=8080. Ignored if --name is unset. - --type string Specify the resource type. E.g. type=svc or type=pod. Ignored if --name is unset. (default "svc") + --local-port int (Optional, autogenerated if not provided) Specify the local port to bind to. E.g. local-port=42000. + --name string Specify the resource name. E.g. name=unicorns or name=unicorn-pod-7448499f4d-b5bk6. Ignored if connect-name is supplied. + --namespace string Specify the namespace. E.g. namespace=default. Ignored if connect-name is supplied. (default "zarf") + --remote-port int Specify the remote port of the resource to bind to. E.g. remote-port=8080. Ignored if connect-name is supplied. + --type string Specify the resource type. E.g. type=svc or type=pod. Ignored if connect-name is supplied. (default "svc") ``` ### Options inherited from parent commands diff --git a/src/cmd/connect.go b/src/cmd/connect.go index 26f012660e..9bc94164bc 100644 --- a/src/cmd/connect.go +++ b/src/cmd/connect.go @@ -16,12 +16,8 @@ import ( ) var ( - connectResourceName string - connectNamespace string - connectResourceType string - connectLocalPort int - connectRemotePort int - cliOnly bool + cliOnly bool + zt cluster.TunnelInfo ) var connectCmd = &cobra.Command{ @@ -46,15 +42,24 @@ var connectCmd = &cobra.Command{ ctx := cmd.Context() var tunnel *cluster.Tunnel - if connectResourceName == "" { - tunnel, err = c.Connect(ctx, target) - } else { - zt := cluster.NewTunnelInfo(connectNamespace, connectResourceType, connectResourceName, "", connectLocalPort, connectRemotePort) + 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) } + if err != nil { return fmt.Errorf("unable to connect to the service: %w", err) } + defer tunnel.Close() // Dump the tunnel URL to the console for other tools to use. @@ -64,11 +69,13 @@ var connectCmd = &cobra.Command{ spinner.Updatef(lang.CmdConnectEstablishedCLI, tunnel.FullURL()) } else { spinner.Updatef(lang.CmdConnectEstablishedWeb, tunnel.FullURL()) + if err := exec.LaunchURL(tunnel.FullURL()); err != nil { message.Debug(err) } } + // Wait for the interrupt signal or an error. select { case <-ctx.Done(): spinner.Successf(lang.CmdConnectTunnelClosed, tunnel.FullURL()) @@ -101,10 +108,10 @@ func init() { rootCmd.AddCommand(connectCmd) connectCmd.AddCommand(connectListCmd) - connectCmd.Flags().StringVar(&connectResourceName, "name", "", lang.CmdConnectFlagName) - connectCmd.Flags().StringVar(&connectNamespace, "namespace", cluster.ZarfNamespaceName, lang.CmdConnectFlagNamespace) - connectCmd.Flags().StringVar(&connectResourceType, "type", cluster.SvcResource, lang.CmdConnectFlagType) - connectCmd.Flags().IntVar(&connectLocalPort, "local-port", 0, lang.CmdConnectFlagLocalPort) - connectCmd.Flags().IntVar(&connectRemotePort, "remote-port", 0, lang.CmdConnectFlagRemotePort) + 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) } diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 5cb2db2d61..e4057e00bd 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -72,11 +72,11 @@ const ( // zarf connect list CmdConnectListShort = "Lists all available connection shortcuts" - CmdConnectFlagName = "Specify the resource name. E.g. name=unicorns or name=unicorn-pod-7448499f4d-b5bk6." - CmdConnectFlagNamespace = "Specify the namespace. E.g. namespace=default. Ignored if --name is unset." - CmdConnectFlagType = "Specify the resource type. E.g. type=svc or type=pod. Ignored if --name is unset." - CmdConnectFlagLocalPort = "(Optional, autogenerated if not provided) Specify the local port to bind to. E.g. local-port=42000. Ignored if --name is unset." - CmdConnectFlagRemotePort = "Specify the remote port of the resource to bind to. E.g. remote-port=8080. Ignored if --name is unset." + CmdConnectFlagName = "Specify the resource name. E.g. name=unicorns or name=unicorn-pod-7448499f4d-b5bk6. Ignored if connect-name is supplied." + CmdConnectFlagNamespace = "Specify the namespace. E.g. namespace=default. Ignored if connect-name is supplied." + CmdConnectFlagType = "Specify the resource type. E.g. type=svc or type=pod. Ignored if connect-name is supplied." + CmdConnectFlagLocalPort = "(Optional, autogenerated if not provided) Specify the local port to bind to. E.g. local-port=42000." + CmdConnectFlagRemotePort = "Specify the remote port of the resource to bind to. E.g. remote-port=8080. Ignored if connect-name is supplied." CmdConnectFlagCliOnly = "Disable browser auto-open" CmdConnectPreparingTunnel = "Preparing a tunnel to connect to %s" diff --git a/src/pkg/cluster/tunnel.go b/src/pkg/cluster/tunnel.go index 5bfab1fd3e..61fd090546 100644 --- a/src/pkg/cluster/tunnel.go +++ b/src/pkg/cluster/tunnel.go @@ -47,26 +47,14 @@ const ( // TunnelInfo is a struct that contains the necessary info to create a new Tunnel type TunnelInfo struct { - localPort int - remotePort int - namespace string - resourceType string - resourceName string + LocalPort int + RemotePort int + Namespace string + ResourceType string + ResourceName string urlSuffix string } -// NewTunnelInfo returns a new TunnelInfo object for connecting to a cluster -func NewTunnelInfo(namespace, resourceType, resourceName, urlSuffix string, localPort, remotePort int) TunnelInfo { - return TunnelInfo{ - namespace: namespace, - resourceType: resourceType, - resourceName: resourceName, - urlSuffix: urlSuffix, - localPort: localPort, - remotePort: remotePort, - } -} - // ListConnections will return a list of all Zarf connect matches found in the cluster. func (c *Cluster) ListConnections(ctx context.Context) (types.ConnectStrings, error) { selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ @@ -93,44 +81,53 @@ func (c *Cluster) ListConnections(ctx context.Context) (types.ConnectStrings, er return connections, nil } -// Connect will establish a tunnel to the specified target. -func (c *Cluster) Connect(ctx context.Context, target string) (*Tunnel, error) { +// NewTargetTunnelInfo returns a new TunnelInfo object for the specified target. +func (c *Cluster) NewTargetTunnelInfo(ctx context.Context, target string) (TunnelInfo, error) { var err error zt := TunnelInfo{ - namespace: ZarfNamespaceName, - resourceType: SvcResource, + Namespace: ZarfNamespaceName, + ResourceType: SvcResource, } switch strings.ToUpper(target) { case ZarfRegistry: - zt.resourceName = ZarfRegistryName - zt.remotePort = ZarfRegistryPort + zt.ResourceName = ZarfRegistryName + zt.RemotePort = ZarfRegistryPort zt.urlSuffix = `/v2/_catalog` case ZarfGit: - zt.resourceName = ZarfGitServerName - zt.remotePort = ZarfGitServerPort + zt.ResourceName = ZarfGitServerName + zt.RemotePort = ZarfGitServerPort case ZarfInjector: - zt.resourceName = ZarfInjectorName - zt.remotePort = ZarfInjectorPort + zt.ResourceName = ZarfInjectorName + zt.RemotePort = ZarfInjectorPort default: if target != "" { if zt, err = c.checkForZarfConnectLabel(ctx, target); err != nil { - return nil, fmt.Errorf("problem looking for a zarf connect label in the cluster: %s", err.Error()) + return TunnelInfo{}, fmt.Errorf("problem looking for a zarf connect label in the cluster: %s", err.Error()) } } - if zt.resourceName == "" { - return nil, fmt.Errorf("missing resource name") + if zt.ResourceName == "" { + return TunnelInfo{}, fmt.Errorf("missing resource name") } - if zt.remotePort < 1 { - return nil, fmt.Errorf("missing remote port") + if zt.RemotePort < 1 { + return TunnelInfo{}, fmt.Errorf("missing remote port") } } + return zt, err +} + +// Connect will establish a tunnel to the specified target. +func (c *Cluster) Connect(ctx context.Context, target string) (*Tunnel, error) { + zt, err := c.NewTargetTunnelInfo(ctx, target) + if err != nil { + return nil, err + } return c.ConnectTunnelInfo(ctx, zt) } // ConnectTunnelInfo connects to the cluster with the provided TunnelInfo func (c *Cluster) ConnectTunnelInfo(ctx context.Context, zt TunnelInfo) (*Tunnel, error) { - tunnel, err := c.NewTunnel(zt.namespace, zt.resourceType, zt.resourceName, zt.urlSuffix, zt.localPort, zt.remotePort) + tunnel, err := c.NewTunnel(zt.Namespace, zt.ResourceType, zt.ResourceName, zt.urlSuffix, zt.LocalPort, zt.RemotePort) if err != nil { return nil, err } @@ -204,25 +201,25 @@ func (c *Cluster) checkForZarfConnectLabel(ctx context.Context, name string) (Tu svc := serviceList.Items[0] // Reset based on the matched params. - zt.resourceType = SvcResource - zt.resourceName = svc.Name - zt.namespace = svc.Namespace + zt.ResourceType = SvcResource + zt.ResourceName = svc.Name + zt.Namespace = svc.Namespace // Only support a service with a single port. - zt.remotePort = svc.Spec.Ports[0].TargetPort.IntValue() + zt.RemotePort = svc.Spec.Ports[0].TargetPort.IntValue() // if targetPort == 0, look for Port (which is required) - if zt.remotePort == 0 { + if zt.RemotePort == 0 { // TODO: Need a check for if container port is not found remotePort, err := c.findPodContainerPort(ctx, svc) if err != nil { return TunnelInfo{}, err } - zt.remotePort = remotePort + zt.RemotePort = remotePort } // Add the url suffix too. zt.urlSuffix = svc.Annotations[ZarfConnectAnnotationURL] - message.Debugf("tunnel connection match: %s/%s on port %d", svc.Namespace, svc.Name, zt.remotePort) + message.Debugf("tunnel connection match: %s/%s on port %d", svc.Namespace, svc.Name, zt.RemotePort) } else { return zt, fmt.Errorf("no matching services found for %s", name) }