Skip to content

Commit 7aa5861

Browse files
authored
feat(instance): routed ip support (#3297)
1 parent 2e1cc44 commit 7aa5861

31 files changed

+37000
-6157
lines changed

cmd/scw/testdata/test-all-usage-instance-server-create-usage.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ ARGS:
4040
[bootscript-id] The bootscript ID to use, if empty the local boot will be used
4141
[cloud-init] The cloud-init script to use (Support file loading with @/path/to/file)
4242
[boot-type=local] The boot type to use, if empty the local boot will be used. Will be overwritten to bootscript if bootscript-id is set. (local | bootscript | rescue)
43+
[routed-ip-enabled] Enable routed IP support
4344
[project-id] Project ID to use. If none is passed the default project ID will be used
4445
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config
4546
[organization-id] Organization ID to use. If none is passed the default organization ID will be used
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
2+
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
3+
Enable routed IP for this server and migrate the nat public IP to routed
4+
Server will reboot !
5+
https://www.scaleway.com/en/docs/compute/instances/api-cli/using-ip-mobility/
6+
7+
USAGE:
8+
scw instance server enable-routed-ip <server-id ...> [arg=value ...]
9+
10+
EXAMPLES:
11+
Migrate a server with legacy network to IP mobility
12+
scw instance server enable-routed-ip 11111111-1111-1111-1111-111111111111
13+
14+
ARGS:
15+
server-id ID of the server affected by the action.
16+
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config
17+
18+
FLAGS:
19+
-h, --help help for enable-routed-ip
20+
-w, --wait wait until the server is ready
21+
22+
GLOBAL FLAGS:
23+
-c, --config string The path to the config file
24+
-D, --debug Enable debug mode
25+
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
26+
-p, --profile string The config profile to use

cmd/scw/testdata/test-all-usage-instance-server-usage.golden

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,28 @@ USAGE:
88
scw instance server <command>
99

1010
AVAILABLE COMMANDS:
11-
attach-ip Attach an IP to a server
12-
attach-volume Attach a volume to a server
13-
backup Backup server
14-
console Connect to the serial console of an instance
15-
create Create server
16-
delete Delete server
17-
detach-ip Detach an IP from a server
18-
detach-volume Detach a volume from its server
19-
get Get an Instance
20-
list List all Instances
21-
list-actions List Instance actions
22-
reboot Reboot server
23-
ssh SSH into a server
24-
standby Put server in standby mode
25-
start Power on server
26-
stop Power off server
27-
terminate Terminate server
28-
update Update an Instance
11+
attach-ip Attach an IP to a server
12+
attach-volume Attach a volume to a server
13+
backup Backup server
14+
console Connect to the serial console of an instance
15+
create Create server
16+
delete Delete server
17+
detach-ip Detach an IP from a server
18+
detach-volume Detach a volume from its server
19+
enable-routed-ip Migrate server to IP mobility
20+
get Get an Instance
21+
list List all Instances
22+
list-actions List Instance actions
23+
reboot Reboot server
24+
ssh SSH into a server
25+
standby Put server in standby mode
26+
start Power on server
27+
stop Power off server
28+
terminate Terminate server
29+
update Update an Instance
2930

3031
WORKFLOW COMMANDS:
31-
wait Wait for server to reach a stable state
32+
wait Wait for server to reach a stable state
3233

3334
FLAGS:
3435
-h, --help help for server

docs/commands/instance.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Instance API.
5656
- [Delete server](#delete-server)
5757
- [Detach an IP from a server](#detach-an-ip-from-a-server)
5858
- [Detach a volume from its server](#detach-a-volume-from-its-server)
59+
- [Migrate server to IP mobility](#migrate-server-to-ip-mobility)
5960
- [Get an Instance](#get-an-instance)
6061
- [List all Instances](#list-all-instances)
6162
- [List Instance actions](#list-instance-actions)
@@ -1715,6 +1716,7 @@ scw instance server create [arg=value ...]
17151716
| bootscript-id | | The bootscript ID to use, if empty the local boot will be used |
17161717
| cloud-init | | The cloud-init script to use |
17171718
| boot-type | Default: `local`<br />One of: `local`, `bootscript`, `rescue` | The boot type to use, if empty the local boot will be used. Will be overwritten to bootscript if bootscript-id is set. |
1719+
| routed-ip-enabled | | Enable routed IP support |
17181720
| project-id | | Project ID to use. If none is passed the default project ID will be used |
17191721
| zone | Default: `fr-par-1` | Zone to target. If none is passed will use default zone from the config |
17201722
| organization-id | | Organization ID to use. If none is passed the default organization ID will be used |
@@ -1855,6 +1857,39 @@ scw instance server detach-volume volume-id=22222222-1111-5555-2222-666666111111
18551857

18561858

18571859

1860+
### Migrate server to IP mobility
1861+
1862+
Enable routed IP for this server and migrate the nat public IP to routed
1863+
Server will reboot !
1864+
https://www.scaleway.com/en/docs/compute/instances/api-cli/using-ip-mobility/
1865+
1866+
1867+
**Usage:**
1868+
1869+
```
1870+
scw instance server enable-routed-ip <server-id ...> [arg=value ...]
1871+
```
1872+
1873+
1874+
**Args:**
1875+
1876+
| Name | | Description |
1877+
|------|---|-------------|
1878+
| server-id | Required | ID of the server affected by the action. |
1879+
| zone | Default: `fr-par-1` | Zone to target. If none is passed will use default zone from the config |
1880+
1881+
1882+
**Examples:**
1883+
1884+
1885+
Migrate a server with legacy network to IP mobility
1886+
```
1887+
scw instance server enable-routed-ip 11111111-1111-1111-1111-111111111111
1888+
```
1889+
1890+
1891+
1892+
18581893
### Get an Instance
18591894

18601895
Get the details of a specified Instance.

internal/human/marshal_func.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ func init() {
8080
})
8181
marshalerFuncs.Store(reflect.TypeOf(scw.IPNet{}), func(i interface{}, opt *MarshalOpt) (string, error) {
8282
v := i.(scw.IPNet)
83-
return v.String(), nil
83+
str := v.String()
84+
if str == "<nil>" {
85+
return "-", nil
86+
}
87+
88+
return str, nil
8489
})
8590
marshalerFuncs.Store(reflect.TypeOf(version.Version{}), func(i interface{}, opt *MarshalOpt) (string, error) {
8691
v := i.(version.Version)

internal/namespaces/instance/v1/custom.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func GetCommands() *core.Commands {
6060
serverStopCommand(),
6161
serverStandbyCommand(),
6262
serverRebootCommand(),
63+
serverEnableRoutedIPCommand(),
6364
serverWaitCommand(),
6465
serverAttachIPCommand(),
6566
serverDetachIPCommand(),

internal/namespaces/instance/v1/custom_server.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func serversMarshalerFunc(i interface{}, opt *human.MarshalOpt) (string, error)
6666
PrivateIP *string
6767
Tags []string
6868
ImageName string
69+
RoutedIPEnabled bool
6970
PlacementGroup *instance.PlacementGroup
7071
ModificationDate *time.Time
7172
CreationDate *time.Time
@@ -101,6 +102,7 @@ func serversMarshalerFunc(i interface{}, opt *human.MarshalOpt) (string, error)
101102
PrivateIP: server.PrivateIP,
102103
Tags: server.Tags,
103104
ImageName: serverImageName,
105+
RoutedIPEnabled: server.RoutedIPEnabled,
104106
PlacementGroup: server.PlacementGroup,
105107
ModificationDate: server.ModificationDate,
106108
CreationDate: server.CreationDate,
@@ -395,8 +397,8 @@ func serverGetBuilder(c *core.Command) *core.Command {
395397
Title: "Volumes",
396398
},
397399
{
398-
Title: "Public IP",
399-
FieldName: "PublicIP",
400+
Title: "Public IPs",
401+
FieldName: "PublicIPs",
400402
},
401403
{
402404
Title: "IPv6",
@@ -729,6 +731,29 @@ func serverRebootCommand() *core.Command {
729731
}
730732
}
731733

734+
func serverEnableRoutedIPCommand() *core.Command {
735+
return &core.Command{
736+
Short: `Migrate server to IP mobility`,
737+
Long: `Enable routed IP for this server and migrate the nat public IP to routed
738+
Server will reboot !
739+
https://www.scaleway.com/en/docs/compute/instances/api-cli/using-ip-mobility/
740+
`,
741+
Namespace: "instance",
742+
Resource: "server",
743+
Verb: "enable-routed-ip",
744+
ArgsType: reflect.TypeOf(instanceActionRequest{}),
745+
Run: getRunServerAction("enable_routed_ip"),
746+
WaitFunc: waitForServerFunc(),
747+
ArgSpecs: serverActionArgSpecs,
748+
Examples: []*core.Example{
749+
{
750+
Short: "Migrate a server with legacy network to IP mobility",
751+
ArgsJSON: `{"server_id": "11111111-1111-1111-1111-111111111111"}`,
752+
},
753+
},
754+
}
755+
}
756+
732757
func serverBackupCommand() *core.Command {
733758
type instanceBackupRequest struct {
734759
Zone scw.Zone

internal/namespaces/instance/v1/custom_server_create.go

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type instanceCreateServerRequest struct {
3232
Stopped bool
3333
SecurityGroupID string
3434
PlacementGroupID string
35+
36+
// IP Mobility
37+
RoutedIPEnabled *bool
38+
3539
// Deprecated
3640
BootscriptID string
3741
CloudInit string
@@ -121,6 +125,10 @@ func serverCreateCommand() *core.Command {
121125
Default: core.DefaultValueSetter(instance.BootTypeLocal.String()),
122126
EnumValues: []string{instance.BootTypeLocal.String(), instance.BootTypeBootscript.String(), instance.BootTypeRescue.String()},
123127
},
128+
{
129+
Name: "routed-ip-enabled",
130+
Short: "Enable routed IP support",
131+
},
124132
core.ProjectIDArgSpec(),
125133
core.ZoneArgSpec(),
126134
core.OrganizationIDArgSpec(),
@@ -182,13 +190,14 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac
182190
needIPCreation := false
183191

184192
serverReq := &instance.CreateServerRequest{
185-
Zone: args.Zone,
186-
Organization: args.OrganizationID,
187-
Project: args.ProjectID,
188-
Name: args.Name,
189-
CommercialType: args.Type,
190-
EnableIPv6: args.IPv6,
191-
Tags: args.Tags,
193+
Zone: args.Zone,
194+
Organization: args.OrganizationID,
195+
Project: args.ProjectID,
196+
Name: args.Name,
197+
CommercialType: args.Type,
198+
EnableIPv6: args.IPv6,
199+
Tags: args.Tags,
200+
RoutedIPEnabled: args.RoutedIPEnabled,
192201
}
193202

194203
client := core.ExtractClient(ctx)
@@ -356,15 +365,11 @@ func instanceServerCreateRun(ctx context.Context, argsI interface{}) (i interfac
356365
if needIPCreation {
357366
logger.Debugf("creating IP")
358367

359-
res, err := apiInstance.CreateIP(&instance.CreateIPRequest{
360-
Zone: args.Zone,
361-
Project: args.ProjectID,
362-
Organization: args.OrganizationID,
363-
})
368+
ip, err := instanceServerCreateIPCreate(args, apiInstance)
364369
if err != nil {
365370
return nil, fmt.Errorf("error while creating your public IP: %s", err)
366371
}
367-
serverReq.PublicIP = scw.StringPtr(res.IP.ID)
372+
serverReq.PublicIP = scw.StringPtr(ip.ID)
368373
logger.Debugf("IP created: %s", serverReq.PublicIP)
369374
}
370375

@@ -714,3 +719,22 @@ func getServerType(apiInstance *instance.API, zone scw.Zone, commercialType stri
714719

715720
return serverType
716721
}
722+
723+
func instanceServerCreateIPCreate(args *instanceCreateServerRequest, api *instance.API) (*instance.IP, error) {
724+
req := &instance.CreateIPRequest{
725+
Zone: args.Zone,
726+
Project: args.ProjectID,
727+
Organization: args.OrganizationID,
728+
}
729+
730+
if args.RoutedIPEnabled != nil && *args.RoutedIPEnabled {
731+
req.Type = instance.IPTypeRoutedIPv4
732+
}
733+
734+
res, err := api.CreateIP(req)
735+
if err != nil {
736+
return nil, err
737+
}
738+
739+
return res.IP, nil
740+
}

internal/namespaces/instance/v1/custom_server_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,34 @@ func Test_ServerBackup(t *testing.T) {
394394
),
395395
}))
396396
}
397+
398+
func Test_ServerEnableRoutedIP(t *testing.T) {
399+
t.Run("simple", core.Test(&core.TestConfig{
400+
Commands: GetCommands(),
401+
BeforeFunc: core.ExecStoreBeforeCmd("Server", "scw instance server create zone=fr-par-3 type=PRO2-XXS image=ubuntu_jammy ip=new --wait"),
402+
Cmd: `scw instance server enable-routed-ip zone=fr-par-3 {{ .Server.ID }} --wait`,
403+
Check: core.TestCheckCombine(
404+
func(t *testing.T, ctx *core.CheckFuncCtx) {
405+
storedServer := ctx.Meta["Server"].(*instance.Server)
406+
api := instance.NewAPI(ctx.Client)
407+
server, err := api.GetServer(&instance.GetServerRequest{
408+
Zone: storedServer.Zone,
409+
ServerID: storedServer.ID,
410+
})
411+
assert.Nil(t, err)
412+
assert.Equal(t, true, server.Server.RoutedIPEnabled)
413+
ip, err := api.GetIP(&instance.GetIPRequest{
414+
Zone: storedServer.Zone,
415+
IP: storedServer.PublicIP.ID,
416+
})
417+
assert.Nil(t, err)
418+
assert.Equal(t, instance.IPTypeRoutedIPv4, ip.IP.Type)
419+
},
420+
core.TestCheckGolden(),
421+
core.TestCheckExitCode(0),
422+
),
423+
AfterFunc: core.AfterFuncCombine(
424+
core.ExecAfterCmd("scw instance server delete zone=fr-par-3 {{ .Server.ID }} force-shutdown=true with-ip=true with-volumes=local"),
425+
),
426+
}))
427+
}

0 commit comments

Comments
 (0)