Skip to content

Commit 5b4fefb

Browse files
authored
feat(as): add ssh verb on the server resource to connect to a server (#1741)
1 parent 168d2e6 commit 5b4fefb

10 files changed

Lines changed: 574 additions & 7 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Connect to distant server via the SSH protocol.
4+
5+
USAGE:
6+
scw apple-silicon server ssh <server-id ...> [arg=value ...]
7+
8+
ARGS:
9+
server-id Server ID to SSH into
10+
[username=m1] Username used for the SSH connection
11+
[port=22] Port used for the SSH connection
12+
[command] Command to execute on the remote server
13+
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config
14+
15+
FLAGS:
16+
-h, --help help for ssh
17+
18+
GLOBAL FLAGS:
19+
-c, --config string The path to the config file
20+
-D, --debug Enable debug mode
21+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
22+
-p, --profile string The config profile to use

cmd/scw/testdata/test-all-usage-apple-silicon-server-usage.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ AVAILABLE COMMANDS:
1212
list List all servers
1313
reboot Reboot a server
1414
reinstall Reinstall a server
15+
ssh SSH into a server
1516
update Update a server
1617
wait Wait for a server to reach a stable state
1718

internal/core/testing.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,11 @@ func createTestClient(t *testing.T, testConfig *TestConfig, httpClient *http.Cli
234234
clientOpts = append(clientOpts, scw.WithEnv())
235235
config, err := scw.LoadConfig()
236236
if err == nil {
237-
p, err := config.GetActiveProfile()
237+
activeProfile, err := config.GetActiveProfile()
238238
require.NoError(t, err)
239-
clientOpts = append(clientOpts, scw.WithProfile(p))
239+
envProfile := scw.LoadEnvProfile()
240+
profile := scw.MergeProfiles(activeProfile, envProfile)
241+
clientOpts = append(clientOpts, scw.WithProfile(profile))
240242
}
241243
}
242244
}

internal/namespaces/applesilicon/v1alpha1/custom.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import (
99
func GetCommands() *core.Commands {
1010
cmds := GetGeneratedCommands()
1111

12-
cmds.Merge(
13-
core.NewCommands(
14-
serverWaitCommand(),
15-
),
16-
)
12+
cmds.Merge(core.NewCommands(
13+
serverSSHCommand(),
14+
serverWaitCommand(),
15+
))
1716

1817
human.RegisterMarshalerFunc(applesilicon.ServerTypeCPU{}, cpuMarshalerFunc)
1918
human.RegisterMarshalerFunc(applesilicon.ServerTypeDisk{}, diskMarshalerFunc)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package applesilicon
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os/exec"
7+
"reflect"
8+
9+
"github.com/scaleway/scaleway-cli/internal/core"
10+
applesilicon "github.com/scaleway/scaleway-sdk-go/api/applesilicon/v1alpha1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
)
13+
14+
type serverSSHConnectRequest struct {
15+
Zone scw.Zone
16+
ServerID string
17+
Username string
18+
Port uint
19+
Command string
20+
}
21+
22+
func serverSSHCommand() *core.Command {
23+
return &core.Command{
24+
Short: `SSH into a server`,
25+
Long: `Connect to distant server via the SSH protocol.`,
26+
Namespace: "apple-silicon",
27+
Verb: "ssh",
28+
Resource: "server",
29+
ArgsType: reflect.TypeOf(serverSSHConnectRequest{}),
30+
ArgSpecs: core.ArgSpecs{
31+
{
32+
Name: "server-id",
33+
Short: "Server ID to SSH into",
34+
Required: true,
35+
Positional: true,
36+
},
37+
{
38+
Name: "username",
39+
Short: "Username used for the SSH connection",
40+
Default: core.DefaultValueSetter("m1"),
41+
},
42+
{
43+
Name: "port",
44+
Short: "Port used for the SSH connection",
45+
Default: core.DefaultValueSetter("22"),
46+
},
47+
{
48+
Name: "command",
49+
Short: "Command to execute on the remote server",
50+
},
51+
core.ZoneArgSpec(),
52+
},
53+
Run: serverSSHRun,
54+
}
55+
}
56+
57+
func serverSSHRun(ctx context.Context, argsI interface{}) (i interface{}, e error) {
58+
args := argsI.(*serverSSHConnectRequest)
59+
60+
client := core.ExtractClient(ctx)
61+
asAPI := applesilicon.NewAPI(client)
62+
serverResp, err := asAPI.GetServer(&applesilicon.GetServerRequest{
63+
Zone: args.Zone,
64+
ServerID: args.ServerID,
65+
})
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
if serverResp.Status != applesilicon.ServerStatusReady {
71+
return nil, &core.CliError{
72+
Err: fmt.Errorf("server is not ready"),
73+
Details: fmt.Sprintf("Server %s currently in %s", serverResp.Name, serverResp.Status),
74+
}
75+
}
76+
77+
sshArgs := []string{
78+
serverResp.IP.String(),
79+
"-p", fmt.Sprintf("%d", args.Port),
80+
"-l", args.Username,
81+
"-t",
82+
}
83+
if args.Command != "" {
84+
sshArgs = append(sshArgs, args.Command)
85+
}
86+
87+
sshCmd := exec.Command("ssh", sshArgs...)
88+
89+
exitCode, err := core.ExecCmd(ctx, sshCmd)
90+
if err != nil {
91+
return nil, err
92+
}
93+
if exitCode != 0 {
94+
return nil, &core.CliError{Empty: true, Code: exitCode}
95+
}
96+
97+
return &core.SuccessResult{Empty: true}, nil
98+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package applesilicon
2+
3+
import (
4+
"testing"
5+
6+
"github.com/scaleway/scaleway-cli/internal/core"
7+
)
8+
9+
func Test_ServerSSH(t *testing.T) {
10+
t.Run("Simple", core.Test(&core.TestConfig{
11+
Commands: GetCommands(),
12+
BeforeFunc: core.BeforeFuncCombine(
13+
core.ExecStoreBeforeCmd("Server", "scw apple-silicon server create --wait"),
14+
),
15+
Cmd: "scw apple-silicon server ssh {{ .Server.ID }}",
16+
OverrideExec: core.OverrideExecSimple(
17+
"ssh {{ .Server.IP }} -p 22 -l m1 -t",
18+
0,
19+
),
20+
Check: core.TestCheckCombine(
21+
core.TestCheckGolden(),
22+
core.TestCheckExitCode(0),
23+
),
24+
AfterFunc: core.AfterFuncCombine(
25+
core.ExecAfterCmd("scw apple-silicon server delete {{ .Server.ID }}"),
26+
),
27+
DisableParallel: true,
28+
}))
29+
30+
t.Run("With-Exit-Code", core.Test(&core.TestConfig{
31+
Commands: GetCommands(),
32+
BeforeFunc: core.BeforeFuncCombine(
33+
core.ExecStoreBeforeCmd("Server", "scw apple-silicon server create --wait"),
34+
),
35+
Cmd: "scw apple-silicon server ssh {{ .Server.ID }}",
36+
OverrideExec: core.OverrideExecSimple(
37+
"ssh {{ .Server.IP }} -p 22 -l m1 -t",
38+
130,
39+
),
40+
Check: core.TestCheckCombine(
41+
core.TestCheckGolden(),
42+
core.TestCheckExitCode(130),
43+
),
44+
AfterFunc: core.AfterFuncCombine(
45+
core.ExecAfterCmd("scw apple-silicon server delete {{ .Server.ID }}"),
46+
),
47+
DisableParallel: true,
48+
}))
49+
}

0 commit comments

Comments
 (0)