-
Notifications
You must be signed in to change notification settings - Fork 18
/
instance_ssh.go
156 lines (121 loc) · 4.09 KB
/
instance_ssh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package cmd
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/kballard/go-shellquote"
"github.com/spf13/cobra"
"github.com/exoscale/cli/pkg/account"
"github.com/exoscale/cli/pkg/globalstate"
"github.com/exoscale/cli/pkg/ssh"
exoapi "github.com/exoscale/egoscale/v2/api"
)
type instanceSSHCmd struct {
cliCommandSettings `cli-cmd:"-"`
sshInfo struct {
ipAddress string
keyFile string
} `cli-cmd:"-"`
_ bool `cli-cmd:"ssh"`
Instance string `cli-arg:"#" cli-usage:"INSTANCE-NAME|ID"`
IPv6 bool `cli-flag:"ipv6" cli-short:"6" cli-help:"connect to the instance via its IPv6 address"`
Login string `cli-short:"l" cli-help:"SSH username to use for logging in (default: instance template default username)"`
PrintCmd bool `cli-flag:"print-command" cli-usage:"print the SSH command that would be executed instead of executing it"`
PrintConfig bool `cli-flag:"print-ssh-config" cli-usage:"print the corresponding SSH information in a format compatible with ssh_config(5)"`
SSHOpts string `cli-flag:"ssh-options" cli-short:"o" cli-usage:"additional options to pass to the ssh(1) command"`
Zone string `cli-short:"z" cli-usage:"instance zone"`
}
func (c *instanceSSHCmd) buildSSHCommand() []string {
cmd := []string{"ssh"}
if _, err := os.Stat(c.sshInfo.keyFile); err == nil {
cmd = append(cmd, "-i", c.sshInfo.keyFile)
}
if c.IPv6 {
cmd = append(cmd, "-6")
}
if c.Login != "" {
cmd = append(cmd, "-l", c.Login)
}
if c.SSHOpts != "" {
opts, err := shellquote.Split(c.SSHOpts)
if err == nil {
cmd = append(cmd, opts...)
}
}
cmd = append(cmd, c.sshInfo.ipAddress)
return cmd
}
func (c *instanceSSHCmd) cmdAliases() []string { return nil }
func (c *instanceSSHCmd) cmdShort() string { return "Log into a Compute instance via SSH" }
func (c *instanceSSHCmd) cmdLong() string {
return `This command connects to a Compute instance via SSH (requires the ssh(1) command).
To pass custom SSH options:
exo compute instance ssh -o "-p 2222 -A" my-instance
`
}
func (c *instanceSSHCmd) cmdPreRun(cmd *cobra.Command, args []string) error {
cmdSetZoneFlagFromDefault(cmd)
return cliCommandDefaultPreRun(c, cmd, args)
}
func (c *instanceSSHCmd) cmdRun(_ *cobra.Command, _ []string) error {
ctx := exoapi.WithEndpoint(gContext, exoapi.NewReqEndpoint(account.CurrentAccount.Environment, c.Zone))
instance, err := globalstate.EgoscaleClient.FindInstance(ctx, c.Zone, c.Instance)
if err != nil {
if errors.Is(err, exoapi.ErrNotFound) {
return fmt.Errorf("resource not found in zone %q", c.Zone)
}
return err
}
// No ssh possible for Private Instances
if *instance.PublicIPAssignment == "none" {
return fmt.Errorf("instance %q is a Private Instance (`exo compute instance ssh` is not supported)", c.Instance)
}
if c.Login == "" {
instanceTemplate, err := globalstate.EgoscaleClient.GetTemplate(ctx, c.Zone, *instance.TemplateID)
if err != nil {
return fmt.Errorf("error retrieving instance template: %w", err)
}
if instanceTemplate.DefaultUser != nil {
c.Login = *instanceTemplate.DefaultUser
}
}
c.sshInfo.keyFile = ssh.GetInstanceSSHKeyPath(*instance.ID)
c.sshInfo.ipAddress = instance.PublicIPAddress.String()
if c.IPv6 {
if instance.IPv6Address == nil {
return fmt.Errorf("instance %q has no IPv6 address", c.Instance)
}
c.sshInfo.ipAddress = instance.IPv6Address.String()
}
sshCmd := c.buildSSHCommand()
switch {
case c.PrintConfig:
out := bytes.NewBuffer(nil)
_, _ = fmt.Fprintf(out, "Host %s\n", c.sshInfo.ipAddress)
if c.Login != "" {
_, _ = fmt.Fprintf(out, "User %s\n", c.Login)
}
if _, err := os.Stat(c.sshInfo.keyFile); err == nil {
_, _ = fmt.Fprintf(out, "IdentityFile %q\n", c.sshInfo.keyFile)
}
fmt.Print(out.String())
return nil
case c.PrintCmd:
fmt.Println(strings.Join(sshCmd, " "))
return nil
default:
cmd := exec.Command("ssh", sshCmd[1:]...)
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
return cmd.Run()
}
}
func init() {
cobra.CheckErr(registerCLICommand(instanceCmd, &instanceSSHCmd{
cliCommandSettings: defaultCLICmdSettings(),
}))
}