diff --git a/CHANGELOG.md b/CHANGELOG.md index e3e78e76..949bf757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Allow downscaling InstancePool and NodePool to 0 +### Features + +- instance: expose a new `console-url` sub-command with a direct link to the VM console on the portal + ## 1.90.1 ### Bug fixes diff --git a/cmd/compute/instance/instance_console_url.go b/cmd/compute/instance/instance_console_url.go new file mode 100644 index 00000000..35f0f234 --- /dev/null +++ b/cmd/compute/instance/instance_console_url.go @@ -0,0 +1,101 @@ +package instance + +import ( + "fmt" + "net/url" + "strings" + + "github.com/spf13/cobra" + + exocmd "github.com/exoscale/cli/cmd" + "github.com/exoscale/cli/pkg/globalstate" + "github.com/exoscale/cli/pkg/output" + v3 "github.com/exoscale/egoscale/v3" +) + +type InstanceConsoleURLOutput struct { + ConsoleURL string `json:"console-url"` +} + +func (o *InstanceConsoleURLOutput) Type() string { return "Compute instance" } +func (o *InstanceConsoleURLOutput) ToJSON() { output.JSON(o) } +func (o *InstanceConsoleURLOutput) ToText() { output.Text(o) } +func (o *InstanceConsoleURLOutput) ToTable() { output.Table(o) } + +type instanceConsoleURLCmd struct { + exocmd.CliCommandSettings `cli-cmd:"-"` + + _ bool `cli-cmd:"console-url"` + + Instance string `cli-arg:"#" cli-usage:"NAME|ID"` + + Zone v3.ZoneName `cli-short:"z" cli-usage:"instance zone"` +} + +func (c *instanceConsoleURLCmd) CmdAliases() []string { return nil } + +func (c *instanceConsoleURLCmd) CmdShort() string { return "Get instance console URL" } + +func (c *instanceConsoleURLCmd) CmdLong() string { + return fmt.Sprintf(`This command generates a Compute instance console URL. + +Supported output template annotations: %s`, + strings.Join(output.TemplateAnnotations(&InstanceConsoleURLOutput{}), ", ")) +} + +func (c *instanceConsoleURLCmd) CmdPreRun(cmd *cobra.Command, args []string) error { + exocmd.CmdSetZoneFlagFromDefault(cmd) + return exocmd.CliCommandDefaultPreRun(c, cmd, args) +} + +func (c *instanceConsoleURLCmd) CmdRun(cmd *cobra.Command, _ []string) error { + ctx := exocmd.GContext + client, err := exocmd.SwitchClientZoneV3(ctx, globalstate.EgoscaleV3Client, c.Zone) + if err != nil { + return err + } + + resp, err := client.ListInstances(ctx) + if err != nil { + return err + } + + foundInstance, err := resp.FindListInstancesResponseInstances(c.Instance) + if err != nil { + return err + } + + consoleProxyURL, err := client.GetConsoleProxyURL(ctx, foundInstance.ID) + + if err != nil { + return err + } + + prefix, _, found := strings.Cut(consoleProxyURL.Host, "console-") + if !found { + prefix = "" + } + + u := &url.URL{ + Scheme: "https", + Host: prefix + "portal.exoscale.com", + Path: "vnc", + } + + q := u.Query() + q.Set("host", consoleProxyURL.Host) + q.Set("path", consoleProxyURL.Path) + u.RawQuery = q.Encode() + + out := InstanceConsoleURLOutput{ + ConsoleURL: u.String(), + } + + return c.OutputFunc(&out, nil) +} + +func init() { + cobra.CheckErr(exocmd.RegisterCLICommand(instanceCmd, &instanceConsoleURLCmd{ + CliCommandSettings: exocmd.DefaultCLICmdSettings(), + })) +}