Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var CopyCmd = &cli.Command{ Name: "copy", Usage: "Copy files between local machine and space", Description: "Copy files to or from a running space. Use spacename:path format for space files.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "workdir", Aliases: []string{"w"}, Usage: "Working directory for relative paths in space", DefaultValue: "", }, }, Arguments: []cli.Argument{ &cli.StringArg{ Name: "source", Required: true, Usage: "Source file path (use spacename:path for space files)", }, &cli.StringArg{ Name: "dest", Required: true, Usage: "Destination file path (use spacename:path for space files)", }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { workdir := cmd.GetString("workdir") source := cmd.GetStringArg("source") dest := cmd.GetStringArg("dest") // Determine direction and extract space name from the path with space: prefix var direction, localPath, spacePath, spaceName string if colonIndex := strings.Index(source, ":"); colonIndex > 0 { direction = "from_space" spaceName = source[:colonIndex] spacePath = source[colonIndex+1:] localPath = dest if spacePath == "" { return fmt.Errorf("Space path cannot be empty after '%s:'", spaceName) } } else if colonIndex := strings.Index(dest, ":"); colonIndex > 0 { direction = "to_space" spaceName = dest[:colonIndex] spacePath = dest[colonIndex+1:] localPath = source if spacePath == "" { return fmt.Errorf("Space path cannot be empty after '%s:'", spaceName) } } else { return fmt.Errorf("One path must use the format 'spacename:path'") } alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } wsUrl := fmt.Sprintf("%s/space-io/%s/copy", cfg.WsServer, spaceId) header := http.Header{ "Authorization": []string{fmt.Sprintf("Bearer %s", cfg.ApiToken)}, } dialer := websocket.DefaultDialer dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: cmd.GetBool("tls-skip-verify")} dialer.HandshakeTimeout = 5 * time.Second ws, response, err := dialer.Dial(wsUrl, header) if err != nil { if response != nil && response.StatusCode == http.StatusUnauthorized { return fmt.Errorf("failed to authenticate with server, check remote token") } else if response != nil && response.StatusCode == http.StatusForbidden { return fmt.Errorf("no permission to copy files in this space") } return fmt.Errorf("Error connecting to websocket: %w", err) } defer ws.Close() var copyRequest apiclient.CopyFileRequest copyRequest.Direction = direction copyRequest.Workdir = workdir if direction == "to_space" { content, err := os.ReadFile(localPath) if err != nil { return fmt.Errorf("Error reading local file: %w", err) } copyRequest.DestPath = spacePath copyRequest.Content = content fmt.Printf("Copying %s to %s:%s...\n", localPath, spaceName, spacePath) } else { copyRequest.SourcePath = spacePath fmt.Printf("Copying %s:%s to %s...\n", spaceName, spacePath, localPath) } err = ws.WriteJSON(copyRequest) if err != nil { return fmt.Errorf("Error sending copy request: %w", err) } // Read the response var result map[string]interface{} err = ws.ReadJSON(&result) if err != nil { return fmt.Errorf("Error reading response: %w", err) } success, ok := result["success"].(bool) if !ok || !success { errorMsg, _ := result["error"].(string) return fmt.Errorf("Copy failed: %s", errorMsg) } if direction == "from_space" { // Write content to local file var content []byte if contentStr, ok := result["content"].(string); ok { // Decode base64 content var err error content, err = base64.StdEncoding.DecodeString(contentStr) if err != nil { return fmt.Errorf("Error decoding file content: %w", err) } } else { return fmt.Errorf("Invalid content format in response") } localDir := filepath.Dir(localPath) if err := os.MkdirAll(localDir, 0755); err != nil { return fmt.Errorf("Error creating local directory: %w", err) } err = os.WriteFile(localPath, content, 0644) if err != nil { return fmt.Errorf("Error writing local file: %w", err) } } fmt.Println("Copy completed successfully") return nil }, }
View Source
var CreateCmd = &cli.Command{ Name: "create", Usage: "Create a space", Description: `Create a new space from the given template. The new space is not started automatically.`, Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the new space to create", Required: true, }, &cli.StringArg{ Name: "template", Usage: "The name of the template to use for the space", Required: true, }, }, MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.StringFlag{ Name: "shell", Usage: "The shell to use for the space (sh, bash, zsh or fish).", ConfigPath: []string{"shell"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_SHELL"}, DefaultValue: "bash", }, }, Run: func(ctx context.Context, cmd *cli.Command) error { shell := cmd.GetString("shell") if shell != "bash" && shell != "zsh" && shell != "fish" && shell != "sh" { return fmt.Errorf("Invalid shell: %s", shell) } fmt.Println("Creating space: ", cmd.GetStringArg("space"), " from template: ", cmd.GetStringArg("template")) alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } templates, _, err := client.GetTemplates(context.Background()) if err != nil { return fmt.Errorf("Error getting templates: %w", err) } // Find the ID of the template from the name var templateId string = "" for _, template := range templates.Templates { if template.Name == cmd.GetStringArg("template") { templateId = template.Id break } } if templateId == "" { return fmt.Errorf("Template not found: %s", cmd.GetStringArg("template")) } space := &apiclient.SpaceRequest{ Name: cmd.GetStringArg("space"), Description: "", TemplateId: templateId, Shell: shell, UserId: "", AltNames: []string{}, } _, _, err = client.CreateSpace(context.Background(), space) if err != nil { return fmt.Errorf("Error creating space: %w", err) } fmt.Println("Space created: ", cmd.GetStringArg("space")) return nil }, }
View Source
var DeleteCmd = &cli.Command{ Name: "delete", Usage: "Delete a space", Description: "Delete a stopped space, all data will be lost.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the new space to create", Required: true, }, }, MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "force", Aliases: []string{"f"}, Usage: "Skip confirmation prompt.", }, }, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") if !cmd.GetBool("force") { var confirm string fmt.Printf("Are you sure you want to delete the space %s and all data? (yes/no): ", spaceName) fmt.Scanln(&confirm) if confirm != "yes" { fmt.Println("Deletion cancelled.") return nil } } alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { fmt.Println("Error getting user: ", err) return nil } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } _, err = client.DeleteSpace(context.Background(), spaceId) if err != nil { return fmt.Errorf("Error deleting space: %w", err) } fmt.Println("Space deleting: ", spaceName) return nil }, }
View Source
var GetFieldCmd = &cli.Command{ Name: "get-field", Usage: "Get a custom field from a space", Description: "Get a custom field value from an existing space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to get the field from", Required: true, }, &cli.StringArg{ Name: "field", Usage: "The name of the custom field to get", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") fieldName := cmd.GetStringArg("field") alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } response, _, err := client.GetSpaceCustomField(context.Background(), spaceId, fieldName) if err != nil { return fmt.Errorf("Error getting custom field: %w", err) } fmt.Println(response.Value) return nil }, }
View Source
var ListCmd = &cli.Command{ Name: "list", Usage: "List the available spaces and their status", Description: "Lists the available spaces for the logged in user and the state of each space.", MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { fmt.Println("Failed to create API client:", err) os.Exit(1) } pingResponse, err := client.Ping(context.Background()) if err != nil { fmt.Println("Error getting server info:", err) os.Exit(1) } zone := pingResponse.Zone user, err := client.WhoAmI(context.Background()) if err != nil { fmt.Println("Error getting user: ", err) return nil } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { fmt.Println("Error getting spaces: ", err) return nil } data := [][]string{{"Name", "Template", "Zone", "Status", "Ports"}} for _, space := range spaces.Spaces { if zone != "" && space.Zone != "" && space.Zone != zone { continue } status := "" ports := make([]string, 0) if space.IsRemote { status = "Remote " } if space.IsDeployed { if space.IsPending { status = status + "Stopping" } else { status = status + "Running" } } else if space.IsDeleting { status = status + "Deleting" } else if space.IsPending { status = status + "Starting" } for port, desc := range space.HttpPorts { var p string if port == desc { p = port } else { p = fmt.Sprintf("%s (%s)", desc, port) } ports = append(ports, p) } for port, desc := range space.TcpPorts { var p string if port == desc { p = port } else { p = fmt.Sprintf("%s (%s)", desc, port) } ports = append(ports, p) } portText := "" if len(ports) > 0 { portText = ports[0] for i := 1; i < len(ports); i++ { portText = portText + ", " + ports[i] } } data = append(data, []string{space.Name, space.TemplateName, space.Zone, status, portText}) } util.PrintTable(data) return nil }, }
View Source
var LogsCmd = &cli.Command{ Name: "logs", Usage: "Show the logs from a space", Description: "Display the logs for a space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to show logs for", Required: true, }, }, MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "follow", Aliases: []string{"f"}, Usage: "Follow the logs.", DefaultValue: false, }, }, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") follow := cmd.GetBool("follow") alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } wsUrl := fmt.Sprintf("%s/logs/%s/stream", cfg.WsServer, spaceId) header := http.Header{"Authorization": []string{fmt.Sprintf("Bearer %s", cfg.ApiToken)}} dialer := websocket.DefaultDialer dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: cmd.GetBool("tls-skip-verify")} dialer.HandshakeTimeout = 5 * time.Second ws, response, err := dialer.Dial(wsUrl, header) if err != nil { if response != nil && response.StatusCode == http.StatusUnauthorized { return fmt.Errorf("failed to authenticate with server, check remote token") } else if response != nil && response.StatusCode == http.StatusForbidden { return fmt.Errorf("no permission to view logs") } return fmt.Errorf("Error connecting to websocket: %w", err) } defer ws.Close() for { _, message, err := ws.ReadMessage() if err != nil { fmt.Println("Error reading message: ", err) break } if len(message) == 1 && message[0] == 0 && !follow { break } fmt.Print(string(message)) } return nil }, }
View Source
var RestartCmd = &cli.Command{ Name: "restart", Usage: "Restart a space", Description: "Restart the named space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to restart", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") fmt.Println("Restarting space: ", spaceName) alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } _, err = client.RestartSpace(context.Background(), spaceId) if err != nil { return fmt.Errorf("Error restarting space: %w", err) } fmt.Println("Space restarting: ", spaceName) return nil }, }
View Source
var RunCmd = &cli.Command{ Name: "run", Usage: "Run a command in a space", Description: "Execute a command within a running space and stream the output.", Flags: []cli.Flag{ &cli.IntFlag{ Name: "timeout", Aliases: []string{"t"}, Usage: "Command timeout in seconds", DefaultValue: 30, }, &cli.StringFlag{ Name: "workdir", Aliases: []string{"w"}, Usage: "Working directory for the command", DefaultValue: "", }, }, Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Required: true, Usage: "The name of the space to run the command in", }, &cli.StringArg{ Name: "command", Required: true, Usage: "The command to run in the space", }, }, MinArgs: 1, MaxArgs: cli.UnlimitedArgs, Run: func(ctx context.Context, cmd *cli.Command) error { timeout := cmd.GetInt("timeout") workdir := cmd.GetString("workdir") spaceName := cmd.GetStringArg("space") command := cmd.GetStringArg("command") alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } wsUrl := fmt.Sprintf("%s/space-io/%s/run", cfg.WsServer, spaceId) header := http.Header{ "Authorization": []string{fmt.Sprintf("Bearer %s", cfg.ApiToken)}, } dialer := websocket.DefaultDialer dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: cmd.GetBool("tls-skip-verify")} dialer.HandshakeTimeout = 5 * time.Second ws, response, err := dialer.Dial(wsUrl, header) if err != nil { if response != nil && response.StatusCode == http.StatusUnauthorized { return fmt.Errorf("failed to authenticate with server, check remote token") } else if response != nil && response.StatusCode == http.StatusForbidden { return fmt.Errorf("no permission to run commands in this space") } return fmt.Errorf("Error connecting to websocket: %w", err) } defer ws.Close() execRequest := apiclient.RunCommandRequest{ Command: command, Args: cmd.GetArgs(), Timeout: timeout, Workdir: workdir, } err = ws.WriteJSON(execRequest) if err != nil { return fmt.Errorf("Error sending command: %w", err) } for { _, message, err := ws.ReadMessage() if err != nil { if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) { break } return fmt.Errorf("Error reading message: %w", err) } if len(message) == 1 && message[0] == 0 { break } fmt.Print(string(message)) } return nil }, }
View Source
var SetFieldCmd = &cli.Command{ Name: "set-field", Usage: "Set a custom field on a space", Description: "Set or update a custom field value on an existing space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to update", Required: true, }, &cli.StringArg{ Name: "field", Usage: "The name of the custom field to set", Required: true, }, &cli.StringArg{ Name: "value", Usage: "The value to set for the custom field", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") fieldName := cmd.GetStringArg("field") fieldValue := cmd.GetStringArg("value") alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } _, err = client.SetSpaceCustomField(context.Background(), spaceId, fieldName, fieldValue) if err != nil { return fmt.Errorf("Error setting custom field: %w", err) } fmt.Printf("Custom field '%s' set to '%s' on space '%s'\n", fieldName, fieldValue, spaceName) return nil }, }
View Source
var SpacesCmd = &cli.Command{ Name: "space", Usage: "Manage spaces", Description: "Manage your spaces from the command line.", Flags: []cli.Flag{ &cli.StringFlag{ Name: "server", Aliases: []string{"s"}, Usage: "The address of the remote server to manage spaces on.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_SERVER"}, Global: true, }, &cli.StringFlag{ Name: "token", Aliases: []string{"t"}, Usage: "The token to use for authentication.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TOKEN"}, Global: true, }, &cli.BoolFlag{ Name: "tls-skip-verify", Usage: "Skip TLS verification when talking to server.", ConfigPath: []string{"tls.skip_verify"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TLS_SKIP_VERIFY"}, DefaultValue: true, Global: true, }, &cli.StringFlag{ Name: "alias", Aliases: []string{"a"}, Usage: "The server alias to use.", DefaultValue: "default", Global: true, }, }, Commands: []*cli.Command{ ListCmd, StartCmd, StopCmd, RestartCmd, CreateCmd, DeleteCmd, LogsCmd, RunCmd, CopyCmd, TunnelPortCmd, SetFieldCmd, GetFieldCmd, }, }
View Source
var StartCmd = &cli.Command{ Name: "start", Usage: "Start a space", Description: "Start the named space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to start", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") fmt.Println("Starting space: ", spaceName) alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } code, err := client.StartSpace(context.Background(), spaceId) if err != nil { if code == 503 { return fmt.Errorf("Cannot start space as outside of schedule") } else if code == 507 { return fmt.Errorf("Cannot start space as resource quota exceeded") } else { return fmt.Errorf("Error starting space: %w", err) } } fmt.Println("Space started: ", spaceName) return nil }, }
View Source
var StopCmd = &cli.Command{ Name: "stop", Usage: "Stop a space", Description: "Stop the named space.", Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to stop", Required: true, }, }, MaxArgs: cli.NoArgs, Run: func(ctx context.Context, cmd *cli.Command) error { spaceName := cmd.GetStringArg("space") fmt.Println("Stopping space: ", spaceName) alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) client, err := apiclient.NewClient(cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify")) if err != nil { return fmt.Errorf("Failed to create API client: %w", err) } user, err := client.WhoAmI(context.Background()) if err != nil { return fmt.Errorf("Error getting user: %w", err) } spaces, _, err := client.GetSpaces(context.Background(), user.Id) if err != nil { return fmt.Errorf("Error getting spaces: %w", err) } // Find the space by name var spaceId string for _, space := range spaces.Spaces { if space.Name == spaceName { spaceId = space.Id break } } if spaceId == "" { return fmt.Errorf("Space not found: %s", spaceName) } _, err = client.StopSpace(context.Background(), spaceId) if err != nil { return fmt.Errorf("Error stopping space: %w", err) } fmt.Println("Space stopped: ", spaceName) return nil }, }
View Source
var TunnelPortCmd = &cli.Command{ Name: "tunnel", Usage: "Tunnel a port to the local machine", Description: `Open a tunnel from a port inside a space to a port on the local machine.`, Arguments: []cli.Argument{ &cli.StringArg{ Name: "space", Usage: "The name of the space to tunnel from", Required: true, }, &cli.IntArg{ Name: "listen", Usage: "The port to listen on within the space", Required: true, }, &cli.IntArg{ Name: "port", Usage: "The local port to connect to", Required: true, }, }, MaxArgs: cli.NoArgs, Flags: []cli.Flag{ &cli.BoolFlag{ Name: "tls", Usage: "Enable TLS encryption for the tunnel.", ConfigPath: []string{"tls"}, EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TUNNEL_TLS"}, DefaultValue: false, }, &cli.StringFlag{ Name: "port-tls-name", Usage: "The name to present to local port when using.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_TLS_NAME"}, }, &cli.BoolFlag{ Name: "port-tls-skip-verify", Usage: "Skip TLS verification when talking to local port via https, this allows self signed certificates.", EnvVars: []string{config.CONFIG_ENV_PREFIX + "_PORT_TLS_SKIP_VERIFY"}, DefaultValue: true, }, }, Run: func(ctx context.Context, cmd *cli.Command) error { alias := cmd.GetString("alias") cfg := config.GetServerAddr(alias, cmd) spaceName := cmd.GetStringArg("space") listenPort := cmd.GetIntArg("listen") if listenPort < 1 || listenPort > 65535 { return fmt.Errorf("Invalid port number, port numbers must be between 1 and 65535") } localPort := cmd.GetIntArg("port") if localPort < 1 || localPort > 65535 { return fmt.Errorf("Invalid port number, port numbers must be between 1 and 65535") } opts := tunnel_server.TunnelOpts{ Type: tunnel_server.PortTunnel, Protocol: "tcp", LocalPort: uint16(localPort), SpaceName: spaceName, SpacePort: uint16(listenPort), TlsName: cmd.GetString("port-tls-name"), TlsSkipVerify: cmd.GetBool("port-tls-skip-verify"), } if cmd.GetBool("tls") { opts.Protocol = "tls" } client := tunnel_server.NewTunnelClient( cfg.WsServer, cfg.HttpServer, cfg.ApiToken, cmd.GetBool("tls-skip-verify"), &opts, ) if err := client.ConnectAndServe(); err != nil { return fmt.Errorf("Failed to create tunnel: %w", err) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) select { case <-client.GetCtx().Done(): case <-c: } client.Shutdown() fmt.Println("\r") log.Info("Tunnel shutdown") return nil }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.