Skip to content

Commit 43780d4

Browse files
Mia-CrossLaure-di
authored andcommitted
feat(k8s): kosmos: add-external-node command (scaleway#4651)
1 parent be9df2f commit 43780d4

File tree

5 files changed

+169
-7
lines changed

5 files changed

+169
-7
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Add an external node to a Kosmos Pool.
4+
This will connect via SSH to the node, download the multicloud configuration script and run it with sudo privileges.
5+
Keep in mind that your external node needs to have wget in order to download the script.
6+
7+
USAGE:
8+
scw k8s pool add-external-node [arg=value ...]
9+
10+
ARGS:
11+
node-ip IP address of the external node
12+
pool-id ID of the Pool the node should be added to
13+
[username=root] Username used for the SSH connection
14+
[region=fr-par] Region to target. If none is passed will use default region from the config
15+
16+
FLAGS:
17+
-h, --help help for add-external-node
18+
19+
GLOBAL FLAGS:
20+
-c, --config string The path to the config file
21+
-D, --debug Enable debug mode
22+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
23+
-p, --profile string The config profile to use

cmd/scw/testdata/test-all-usage-k8s-pool-usage.golden

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ USAGE:
77
scw k8s pool <command>
88

99
AVAILABLE COMMANDS:
10-
create Create a new Pool in a Cluster
11-
delete Delete a Pool in a Cluster
12-
get Get a Pool in a Cluster
13-
list List Pools in a Cluster
14-
update Update a Pool in a Cluster
15-
upgrade Upgrade a Pool in a Cluster
10+
add-external-node Add an external node to a Kosmos Pool
11+
create Create a new Pool in a Cluster
12+
delete Delete a Pool in a Cluster
13+
get Get a Pool in a Cluster
14+
list List Pools in a Cluster
15+
update Update a Pool in a Cluster
16+
upgrade Upgrade a Pool in a Cluster
1617

1718
WORKFLOW COMMANDS:
18-
wait Wait for a pool to reach a stable state
19+
wait Wait for a pool to reach a stable state
1920

2021
FLAGS:
2122
-h, --help help for pool

docs/commands/k8s.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ This API allows you to manage Kubernetes Kapsule and Kosmos clusters.
3434
- [Replace a Node in a Cluster](#replace-a-node-in-a-cluster)
3535
- [Wait for a node to reach a stable state](#wait-for-a-node-to-reach-a-stable-state)
3636
- [Kapsule pool management commands](#kapsule-pool-management-commands)
37+
- [Add an external node to a Kosmos Pool](#add-an-external-node-to-a-kosmos-pool)
3738
- [Create a new Pool in a Cluster](#create-a-new-pool-in-a-cluster)
3839
- [Delete a Pool in a Cluster](#delete-a-pool-in-a-cluster)
3940
- [Get a Pool in a Cluster](#get-a-pool-in-a-cluster)
@@ -961,6 +962,30 @@ A pool is a set of identical nodes
961962
A pool has a name, a size (its desired number of nodes), node number limits (min, max), and a Scaleway Instance type. Changing those limits increases/decreases the size of a pool. As a result and depending on its load, the pool will grow or shrink within those limits when autoscaling is enabled.
962963

963964

965+
### Add an external node to a Kosmos Pool
966+
967+
Add an external node to a Kosmos Pool.
968+
This will connect via SSH to the node, download the multicloud configuration script and run it with sudo privileges.
969+
Keep in mind that your external node needs to have wget in order to download the script.
970+
971+
**Usage:**
972+
973+
```
974+
scw k8s pool add-external-node [arg=value ...]
975+
```
976+
977+
978+
**Args:**
979+
980+
| Name | | Description |
981+
|------|---|-------------|
982+
| node-ip | Required | IP address of the external node |
983+
| pool-id | Required | ID of the Pool the node should be added to |
984+
| username | Default: `root` | Username used for the SSH connection |
985+
| region | Default: `fr-par` | Region to target. If none is passed will use default region from the config |
986+
987+
988+
964989
### Create a new Pool in a Cluster
965990

966991
Create a new pool in a specific Kubernetes cluster.

internal/namespaces/k8s/v1/custom.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func GetCommands() *core.Commands {
2626
k8sClusterWaitCommand(),
2727
k8sNodeWaitCommand(),
2828
k8sPoolWaitCommand(),
29+
k8sPoolAddExternalNodeCommand(),
2930
))
3031

3132
human.RegisterMarshalerFunc(k8s.Version{}, versionMarshalerFunc)

internal/namespaces/k8s/v1/custom_pool.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8+
"os/exec"
89
"reflect"
10+
"strings"
911
"time"
1012

1113
"github.com/fatih/color"
1214
"github.com/scaleway/scaleway-cli/v2/core"
1315
"github.com/scaleway/scaleway-cli/v2/core/human"
16+
"github.com/scaleway/scaleway-cli/v2/internal/interactive"
1417
k8s "github.com/scaleway/scaleway-sdk-go/api/k8s/v1"
1518
"github.com/scaleway/scaleway-sdk-go/scw"
1619
)
@@ -137,3 +140,112 @@ func k8sPoolWaitCommand() *core.Command {
137140
},
138141
}
139142
}
143+
144+
type k8sPoolAddExternalNodeRequest struct {
145+
NodeIP string
146+
PoolID string
147+
Username string
148+
Region scw.Region
149+
}
150+
151+
func k8sPoolAddExternalNodeCommand() *core.Command {
152+
return &core.Command{
153+
Short: `Add an external node to a Kosmos Pool`,
154+
Long: `Add an external node to a Kosmos Pool.
155+
This will connect via SSH to the node, download the multicloud configuration script and run it with sudo privileges.
156+
Keep in mind that your external node needs to have wget in order to download the script.`,
157+
Namespace: "k8s",
158+
Resource: "pool",
159+
Verb: "add-external-node",
160+
ArgsType: reflect.TypeOf(k8sPoolAddExternalNodeRequest{}),
161+
ArgSpecs: core.ArgSpecs{
162+
{
163+
Name: "node-ip",
164+
Short: `IP address of the external node`,
165+
Required: true,
166+
},
167+
{
168+
Name: "pool-id",
169+
Short: `ID of the Pool the node should be added to`,
170+
Required: true,
171+
},
172+
{
173+
Name: "username",
174+
Short: "Username used for the SSH connection",
175+
Default: core.DefaultValueSetter("root"),
176+
},
177+
core.RegionArgSpec(),
178+
},
179+
Run: func(ctx context.Context, argsI interface{}) (i interface{}, err error) {
180+
args := argsI.(*k8sPoolAddExternalNodeRequest)
181+
sshCommonArgs := []string{
182+
args.NodeIP,
183+
"-t",
184+
"-l", args.Username,
185+
}
186+
187+
// Set POOL_ID and REGION in the node init script and copy it to the remote
188+
homeDir := "/root"
189+
if args.Username != "root" {
190+
homeDir = "/home/" + args.Username
191+
}
192+
nodeInitScript := buildNodeInitScript(args.PoolID, args.Region)
193+
copyScriptArgs := []string{
194+
"cat", "<<", "EOF",
195+
">", homeDir + "/init_kosmos_node.sh",
196+
"\n",
197+
}
198+
copyScriptArgs = append(copyScriptArgs, strings.Split(nodeInitScript, " \n")...)
199+
if err = execSSHCommand(ctx, append(sshCommonArgs, copyScriptArgs...), true); err != nil {
200+
return nil, err
201+
}
202+
chmodArgs := []string{"chmod", "+x", homeDir + "/init_kosmos_node.sh"}
203+
if err = execSSHCommand(ctx, append(sshCommonArgs, chmodArgs...), true); err != nil {
204+
return nil, err
205+
}
206+
207+
// Execute the script with SCW_SECRET_KEY set
208+
client := core.ExtractClient(ctx)
209+
secretKey, _ := client.GetSecretKey()
210+
execScriptArgs := []string{
211+
"", // Adding a space to prevent the command from being logged in history as it shows the secret key
212+
"SCW_SECRET_KEY=" + secretKey,
213+
"./init_kosmos_node.sh",
214+
}
215+
if err = execSSHCommand(ctx, append(sshCommonArgs, execScriptArgs...), false); err != nil {
216+
return nil, err
217+
}
218+
219+
return &core.SuccessResult{Empty: true}, nil
220+
},
221+
}
222+
}
223+
224+
func execSSHCommand(ctx context.Context, args []string, printSeparator bool) error {
225+
remoteCmd := exec.Command("ssh", args...)
226+
_, _ = interactive.Println(remoteCmd)
227+
228+
exitCode, err := core.ExecCmd(ctx, remoteCmd)
229+
if err != nil {
230+
return err
231+
}
232+
if exitCode != 0 {
233+
return fmt.Errorf("ssh command failed with exit code %d", exitCode)
234+
}
235+
if printSeparator {
236+
_, _ = interactive.Println("-----")
237+
}
238+
239+
return nil
240+
}
241+
242+
func buildNodeInitScript(poolID string, region scw.Region) string {
243+
return fmt.Sprintf(`#!/usr/bin/env sh
244+
245+
set -e
246+
wget https://scwcontainermulticloud.s3.fr-par.scw.cloud/node-agent_linux_amd64 --no-verbose
247+
chmod +x node-agent_linux_amd64
248+
export POOL_ID=%s POOL_REGION=%s SCW_SECRET_KEY=\$SCW_SECRET_KEY
249+
sudo -E ./node-agent_linux_amd64 -loglevel 0 -no-controller
250+
EOF`, poolID, region.String())
251+
}

0 commit comments

Comments
 (0)