Skip to content

Commit 141fdb1

Browse files
authored
feat(baremetal): add support for add flexible ip (#3670)
1 parent ee2927c commit 141fdb1

15 files changed

+3491
-20
lines changed
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+
Create and attach a new flexible IP to a server
4+
5+
USAGE:
6+
scw baremetal server add-flexible-ip <server-id ...> [arg=value ...]
7+
8+
ARGS:
9+
server-id ID of the server to which the newly created flexible IP will be attached.
10+
[description] Flexible IP description (max. of 255 characters)
11+
[ip-type] Define whether the flexible IP is an IPv4 or IPv6 (IPv4 | IPv6)
12+
[tags.{index}] Tags to associate to the flexible IP
13+
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config (fr-par-1 | fr-par-2 | nl-ams-1)
14+
15+
FLAGS:
16+
-h, --help help for add-flexible-ip
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-baremetal-server-usage.golden

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,24 @@ USAGE:
66
scw baremetal server <command>
77

88
AVAILABLE COMMANDS:
9-
create Create an Elastic Metal server
10-
delete Delete an Elastic Metal server
11-
get Get a specific Elastic Metal server
12-
get-metrics Return server metrics
13-
install Install an Elastic Metal server
14-
list List Elastic Metal servers for an Organization
15-
list-events List server events
16-
reboot Reboot an Elastic Metal server
17-
start Start an Elastic Metal server
18-
stop Stop an Elastic Metal server
19-
update Update an Elastic Metal server
20-
update-ip Update IP
9+
create Create an Elastic Metal server
10+
delete Delete an Elastic Metal server
11+
get Get a specific Elastic Metal server
12+
get-metrics Return server metrics
13+
install Install an Elastic Metal server
14+
list List Elastic Metal servers for an Organization
15+
list-events List server events
16+
reboot Reboot an Elastic Metal server
17+
start Start an Elastic Metal server
18+
stop Stop an Elastic Metal server
19+
update Update an Elastic Metal server
20+
update-ip Update IP
21+
22+
UTILITY COMMANDS:
23+
add-flexible-ip Attach a new flexible IP to a server
2124

2225
WORKFLOW COMMANDS:
23-
wait Wait for a server to reach a stable state (delivery and installation)
26+
wait Wait for a server to reach a stable state (delivery and installation)
2427

2528
FLAGS:
2629
-h, --help help for server

docs/commands/baremetal.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Elastic Metal API.
2323
- [List the Private Networks of a server](#list-the-private-networks-of-a-server)
2424
- [Set multiple Private Networks on a server](#set-multiple-private-networks-on-a-server)
2525
- [Server management commands](#server-management-commands)
26+
- [Attach a new flexible IP to a server](#attach-a-new-flexible-ip-to-a-server)
2627
- [Create an Elastic Metal server](#create-an-elastic-metal-server)
2728
- [Delete an Elastic Metal server](#delete-an-elastic-metal-server)
2829
- [Get a specific Elastic Metal server](#get-a-specific-elastic-metal-server)
@@ -478,6 +479,29 @@ scw baremetal private-network set [arg=value ...]
478479
A server is a denomination of a type of instances provided by Scaleway.
479480

480481

482+
### Attach a new flexible IP to a server
483+
484+
Create and attach a new flexible IP to a server
485+
486+
**Usage:**
487+
488+
```
489+
scw baremetal server add-flexible-ip <server-id ...> [arg=value ...]
490+
```
491+
492+
493+
**Args:**
494+
495+
| Name | | Description |
496+
|------|---|-------------|
497+
| server-id | Required | ID of the server to which the newly created flexible IP will be attached. |
498+
| description | | Flexible IP description (max. of 255 characters) |
499+
| ip-type | One of: `IPv4`, `IPv6` | Define whether the flexible IP is an IPv4 or IPv6 |
500+
| tags.{index} | | Tags to associate to the flexible IP |
501+
| zone | Default: `fr-par-1`<br />One of: `fr-par-1`, `fr-par-2`, `nl-ams-1` | Zone to target. If none is passed will use default zone from the config |
502+
503+
504+
481505
### Create an Elastic Metal server
482506

483507
Create a new Elastic Metal server. Once the server is created, proceed with the [installation of an OS](#post-3e949e).

internal/interactive/list.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package interactive
44

55
import (
6+
"bytes"
67
"context"
78
"fmt"
89
"os"
@@ -78,6 +79,7 @@ func (m *ListPrompt) Execute(ctx context.Context) (int, error) {
7879
ctx: ctx,
7980
defaultReader: os.Stdin,
8081
}))
82+
opts = append(opts, tea.WithOutput(bytes.NewBuffer([]byte{})))
8183
}
8284

8385
p := tea.NewProgram(m, opts...)

internal/namespaces/baremetal/v1/custom.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ func GetCommands() *core.Commands {
1111

1212
cmds.Merge(core.NewCommands(
1313
serverWaitCommand(),
14+
serverAddFlexibleIP(),
1415
))
1516

1617
human.RegisterMarshalerFunc(baremetal.ServerPingStatus(""), human.EnumMarshalFunc(serverPingStatusMarshalSpecs))
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package baremetal
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"reflect"
7+
8+
"github.com/scaleway/scaleway-cli/v2/internal/core"
9+
flexibleip "github.com/scaleway/scaleway-cli/v2/internal/namespaces/flexibleip/v1alpha1"
10+
"github.com/scaleway/scaleway-sdk-go/api/baremetal/v1"
11+
fip "github.com/scaleway/scaleway-sdk-go/api/flexibleip/v1alpha1"
12+
"github.com/scaleway/scaleway-sdk-go/scw"
13+
)
14+
15+
type serverAddFlexibleIPRequest struct {
16+
ServerID string
17+
Description string
18+
IPType string
19+
Tags []string
20+
Zone scw.Zone
21+
}
22+
23+
func serverAddFlexibleIP() *core.Command {
24+
return &core.Command{
25+
Short: "Attach a new flexible IP to a server",
26+
Long: "Create and attach a new flexible IP to a server",
27+
Namespace: "baremetal",
28+
Resource: "server",
29+
Verb: "add-flexible-ip",
30+
Groups: []string{"utility"},
31+
ArgsType: reflect.TypeOf(serverAddFlexibleIPRequest{}),
32+
ArgSpecs: core.ArgSpecs{
33+
{
34+
Name: "server-id",
35+
Short: `ID of the server to which the newly created flexible IP will be attached.`,
36+
Required: true,
37+
Deprecated: false,
38+
Positional: true,
39+
},
40+
{
41+
Name: "description",
42+
Short: `Flexible IP description (max. of 255 characters)`,
43+
Required: false,
44+
Deprecated: false,
45+
Positional: false,
46+
},
47+
{
48+
Name: "ip-type",
49+
Short: `Define whether the flexible IP is an IPv4 or IPv6`,
50+
Required: false,
51+
Deprecated: false,
52+
Positional: false,
53+
EnumValues: ipTypeOption,
54+
ValidateFunc: func(_ *core.ArgSpec, value interface{}) error {
55+
if value == "IPv4" || value == "IPv6" || value == "" {
56+
return nil
57+
}
58+
return &core.CliError{
59+
Err: fmt.Errorf("error looks like the IP type isn't correct"),
60+
Hint: "Two type available : IPv6 or IPv4",
61+
}
62+
},
63+
},
64+
{
65+
Name: "tags.{index}",
66+
Short: `Tags to associate to the flexible IP`,
67+
Required: false,
68+
Deprecated: false,
69+
Positional: false,
70+
},
71+
core.ZoneArgSpec(scw.ZoneFrPar1, scw.ZoneFrPar2, scw.ZoneNlAms1),
72+
},
73+
Run: func(ctx context.Context, argsI interface{}) (interface{}, error) {
74+
request := argsI.(*serverAddFlexibleIPRequest)
75+
client := core.ExtractClient(ctx)
76+
api := baremetal.NewAPI(client)
77+
server, err := api.GetServer(&baremetal.GetServerRequest{
78+
Zone: request.Zone,
79+
ServerID: request.ServerID,
80+
}, scw.WithContext(ctx))
81+
if err != nil {
82+
return nil, err
83+
}
84+
args := request
85+
if args.IPType == "" {
86+
args, err = promptIPFlexibleServer(ctx, request)
87+
}
88+
if err != nil {
89+
return nil, err
90+
}
91+
apiFip := fip.NewAPI(client)
92+
IsIPv6 := args.IPType == "IPv6"
93+
flexibleIP, err := apiFip.CreateFlexibleIP(&fip.CreateFlexibleIPRequest{
94+
ServerID: &server.ID,
95+
Zone: server.Zone,
96+
ProjectID: server.ProjectID,
97+
Description: args.Description,
98+
Tags: args.Tags,
99+
IsIPv6: IsIPv6,
100+
})
101+
if err != nil {
102+
return nil, err
103+
}
104+
return apiFip.WaitForFlexibleIP(&fip.WaitForFlexibleIPRequest{
105+
FipID: flexibleIP.ID,
106+
Zone: flexibleIP.Zone,
107+
Timeout: scw.TimeDurationPtr(flexibleip.FlexibleIPTimeout),
108+
RetryInterval: core.DefaultRetryInterval,
109+
})
110+
},
111+
}
112+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package baremetal
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/fatih/color"
8+
"github.com/scaleway/scaleway-cli/v2/internal/core"
9+
"github.com/scaleway/scaleway-cli/v2/internal/interactive"
10+
"github.com/scaleway/scaleway-cli/v2/internal/terminal"
11+
)
12+
13+
var ipTypeOption = []string{"IPv4", "IPv6"}
14+
15+
func promptIPFlexibleServer(ctx context.Context, req *serverAddFlexibleIPRequest) (*serverAddFlexibleIPRequest, error) {
16+
if !interactive.IsInteractive {
17+
return nil, &core.CliError{
18+
Err: fmt.Errorf("failed to create and attach a new flexible IP"),
19+
Hint: "Missing argument 'ip-type'",
20+
}
21+
}
22+
var err error
23+
quitMessage := terminal.Style("Type Ctrl+c or q to quit", color.FgHiBlue, color.Bold)
24+
fmt.Println(quitMessage)
25+
req.IPType, err = promptChooseIPType(ctx)
26+
if err != nil {
27+
return nil, err
28+
}
29+
return req, nil
30+
}
31+
32+
func promptChooseIPType(ctx context.Context) (string, error) {
33+
prompt := interactive.ListPrompt{
34+
Prompt: "Choose your internet protocol",
35+
Choices: ipTypeOption,
36+
DefaultIndex: 0,
37+
}
38+
ipTypeIndex, err := prompt.Execute(ctx)
39+
if err != nil {
40+
return "", err
41+
}
42+
return ipTypeOption[ipTypeIndex], nil
43+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package baremetal_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/scaleway/scaleway-cli/v2/internal/core"
7+
"github.com/scaleway/scaleway-cli/v2/internal/interactive"
8+
"github.com/scaleway/scaleway-cli/v2/internal/namespaces/baremetal/v1"
9+
flexibleip "github.com/scaleway/scaleway-cli/v2/internal/namespaces/flexibleip/v1alpha1"
10+
)
11+
12+
func Test_CreateFlexibleIPInteractive(t *testing.T) {
13+
promptResponse := []string{
14+
`" "`,
15+
}
16+
interactive.IsInteractive = true
17+
cmds := baremetal.GetCommands()
18+
cmds.Merge(flexibleip.GetCommands())
19+
t.Run("Simple Interactive", core.Test(&core.TestConfig{
20+
Commands: cmds,
21+
BeforeFunc: core.BeforeFuncCombine(
22+
createServerAndWaitDefault("Server"),
23+
),
24+
Cmd: "scw baremetal server add-flexible-ip {{ .Server.ID }}",
25+
Check: core.TestCheckCombine(
26+
core.TestCheckGolden(),
27+
),
28+
AfterFunc: core.AfterFuncCombine(
29+
deleteServerDefault("Server"),
30+
core.ExecAfterCmd("scw fip ip delete {{ .CmdResult.ID }}"),
31+
),
32+
PromptResponseMocks: promptResponse,
33+
}))
34+
}
35+
36+
func Test_CreateFlexibleIP(t *testing.T) {
37+
interactive.IsInteractive = false
38+
cmds := baremetal.GetCommands()
39+
cmds.Merge(flexibleip.GetCommands())
40+
t.Run("Simple", core.Test(&core.TestConfig{
41+
Commands: cmds,
42+
BeforeFunc: core.BeforeFuncCombine(
43+
createServerAndWaitDefault("Server"),
44+
),
45+
Cmd: "scw baremetal server add-flexible-ip {{ .Server.ID }} ip-type=IPv4",
46+
Check: core.TestCheckCombine(
47+
core.TestCheckGolden(),
48+
),
49+
AfterFunc: core.AfterFuncCombine(
50+
deleteServerDefault("Server"),
51+
core.ExecAfterCmd("scw fip ip delete {{ .CmdResult.ID }}"),
52+
),
53+
}))
54+
}

internal/namespaces/baremetal/v1/helpers_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package baremetal_test
22

33
import (
4+
"fmt"
5+
46
"github.com/scaleway/scaleway-cli/v2/internal/core"
57
)
68

@@ -10,6 +12,10 @@ func createServerAndWait(metaKey string) core.BeforeFunc {
1012
return core.ExecStoreBeforeCmd(metaKey, "scw baremetal server create zone=nl-ams-1 type=GP-BM2-S -w")
1113
}
1214

15+
func createServerAndWaitDefault(metaKey string) core.BeforeFunc {
16+
return core.ExecStoreBeforeCmd(metaKey, "scw baremetal server create type=EM-B112X-SSD -w")
17+
}
18+
1319
func createServer(metaKey string) core.BeforeFunc {
1420
return core.ExecStoreBeforeCmd(metaKey, "scw baremetal server create zone=nl-ams-1 type=GP-BM2-S")
1521
}
@@ -19,7 +25,11 @@ func createServer(metaKey string) core.BeforeFunc {
1925
//
2026
//nolint:unparam
2127
func deleteServer(metaKey string) core.AfterFunc {
22-
return core.ExecAfterCmd("scw baremetal server delete zone=nl-ams-1 {{ ." + metaKey + ".ID }}")
28+
return core.ExecAfterCmd(fmt.Sprintf("scw baremetal server delete zone=nl-ams-1 {{ ." + metaKey + ".ID }}"))
29+
}
30+
31+
func deleteServerDefault(metaKey string) core.AfterFunc {
32+
return core.ExecAfterCmd(fmt.Sprintf("scw baremetal server delete {{ ." + metaKey + ".ID }}"))
2333
}
2434

2535
// add an ssh key with a given meta key
@@ -34,5 +44,5 @@ func addSSH(metaKey string, key string) core.BeforeFunc {
3444

3545
// delete an ssh key with a given meta key
3646
func deleteSSH(metaKey string) core.AfterFunc {
37-
return core.ExecAfterCmd("scw iam ssh-key delete {{ ." + metaKey + ".ID }}")
47+
return core.ExecAfterCmd(fmt.Sprintf("scw iam ssh-key delete {{ ." + metaKey + ".ID }}"))
3848
}

0 commit comments

Comments
 (0)