Skip to content

Commit 47cd7de

Browse files
feat(instance): update server volume ids (#698)
1 parent e83eb37 commit 47cd7de

File tree

7 files changed

+2978
-36
lines changed

7 files changed

+2978
-36
lines changed

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
7272
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7373
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
7474
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
75-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.5.0.20200211104822-047c88bb15c4 h1:7MhGZPXmbEqx97kIrBA7Xen1obsqQPnR0HKErxh76S4=
76-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.5.0.20200211104822-047c88bb15c4/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
7775
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.5.0.20200212160027-75ab2f6aeda3 h1:BQb9gbsenI8ZwZmfN7O93VrlXW8eiDlL7Mz+glsK/os=
7876
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.5.0.20200212160027-75ab2f6aeda3/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
7977
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=

internal/core/arg_specs.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package core
22

3-
import "github.com/scaleway/scaleway-sdk-go/scw"
3+
import (
4+
"fmt"
5+
6+
"github.com/scaleway/scaleway-sdk-go/scw"
7+
)
48

59
type ArgSpecs []*ArgSpec
610

@@ -20,6 +24,21 @@ func (s *ArgSpecs) DeleteByName(name string) {
2024
return
2125
}
2226
}
27+
panic(fmt.Errorf("in DeleteByName: %s not found", name))
28+
}
29+
30+
func (s *ArgSpecs) AddBefore(name string, argSpec *ArgSpec) {
31+
for i, spec := range *s {
32+
if spec.Name == name {
33+
newSpecs := ArgSpecs(nil)
34+
newSpecs = append(newSpecs, (*s)[:i]...)
35+
newSpecs = append(newSpecs, argSpec)
36+
newSpecs = append(newSpecs, (*s)[i:]...)
37+
*s = newSpecs
38+
return
39+
}
40+
}
41+
panic(fmt.Errorf("in AddBefore: %s not found", name))
2342
}
2443

2544
type ArgSpec struct {

internal/namespaces/instance/v1/custom_server.go

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net"
77
"reflect"
88
"sort"
9+
"strconv"
910
"time"
1011

1112
"github.com/fatih/color"
@@ -178,28 +179,43 @@ func serverUpdateBuilder(c *core.Command) *core.Command {
178179
*instance.UpdateServerRequest
179180
IP *instance.NullableStringValue
180181
PlacementGroupID *instance.NullableStringValue
181-
SecurityGroupID string
182+
SecurityGroupID *string
183+
VolumeIDs *[]string
182184
}
183185

184-
IPArgSpec := &core.ArgSpec{
185-
Name: "ip",
186-
Short: `IP that should be attached to the server (use ip=none to remove)`,
187-
}
188-
c.ArgSpecs.GetByName("placement-group").Name = "placement-group-id"
189-
190186
c.ArgsType = reflect.TypeOf(instanceUpdateServerRequestCustom{})
191187

192-
c.ArgSpecs = append(c.ArgSpecs, IPArgSpec)
193-
c.ArgSpecs.DeleteByName("security-group.name")
188+
// Rename modified arg specs.
189+
c.ArgSpecs.GetByName("placement-group").Name = "placement-group-id"
194190
c.ArgSpecs.GetByName("security-group.id").Name = "security-group-id"
195191

192+
// Delete unused arg specs.
193+
c.ArgSpecs.DeleteByName("security-group.name")
194+
c.ArgSpecs.DeleteByName("volumes.{key}.name")
195+
c.ArgSpecs.DeleteByName("volumes.{key}.size")
196+
c.ArgSpecs.DeleteByName("volumes.{key}.id")
197+
c.ArgSpecs.DeleteByName("volumes.{key}.volume-type")
198+
c.ArgSpecs.DeleteByName("volumes.{key}.organization")
199+
200+
// Add new arg specs.
201+
c.ArgSpecs.AddBefore("placement-group-id", &core.ArgSpec{
202+
Name: "volume-ids.{index}",
203+
Short: "Will update ALL volume IDs at once, including the root volume of the server (use volume-ids=none to detach all volumes)",
204+
})
205+
c.ArgSpecs.AddBefore("boot-type", &core.ArgSpec{
206+
Name: "ip",
207+
Short: `IP that should be attached to the server (use ip=none to detach)`,
208+
})
209+
196210
c.Run = func(ctx context.Context, argsI interface{}) (i interface{}, e error) {
197211
customRequest := argsI.(*instanceUpdateServerRequestCustom)
198212

199213
updateServerRequest := customRequest.UpdateServerRequest
200214
updateServerRequest.PlacementGroup = customRequest.PlacementGroupID
201-
updateServerRequest.SecurityGroup = &instance.SecurityGroupTemplate{
202-
ID: customRequest.SecurityGroupID,
215+
if customRequest.SecurityGroupID != nil {
216+
updateServerRequest.SecurityGroup = &instance.SecurityGroupTemplate{
217+
ID: *customRequest.SecurityGroupID,
218+
}
203219
}
204220

205221
attachIPRequest := (*instance.UpdateIPRequest)(nil)
@@ -210,11 +226,11 @@ func serverUpdateBuilder(c *core.Command) *core.Command {
210226
api := instance.NewAPI(client)
211227

212228
getServerResponse, err := api.GetServer(&instance.GetServerRequest{
213-
Zone: "",
229+
Zone: updateServerRequest.Zone,
214230
ServerID: customRequest.ServerID,
215231
})
216232
if err != nil {
217-
return "", err
233+
return nil, err
218234
}
219235

220236
switch {
@@ -255,20 +271,30 @@ func serverUpdateBuilder(c *core.Command) *core.Command {
255271
},
256272
})
257273
if err != nil {
258-
return "", err
274+
return nil, err
259275
}
260276
}
261277

262278
if attachIPRequest != nil {
263279
_, err = api.UpdateIP(attachIPRequest)
264280
if err != nil {
265-
return "", err
281+
return nil, err
282+
}
283+
}
284+
285+
// Update all volume IDs at once.
286+
if customRequest.VolumeIDs != nil {
287+
volumes := make(map[string]*instance.VolumeTemplate)
288+
for i, volumeID := range *customRequest.VolumeIDs {
289+
index := strconv.Itoa(i)
290+
volumes[index] = &instance.VolumeTemplate{ID: volumeID, Name: getServerResponse.Server.Name + "-" + index}
266291
}
292+
customRequest.Volumes = &volumes
267293
}
268294

269295
updateServerResponse, err := api.UpdateServer(updateServerRequest)
270296
if err != nil {
271-
return "", err
297+
return nil, err
272298
}
273299

274300
return updateServerResponse, nil

internal/namespaces/instance/v1/custom_server_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ func Test_ServerVolumeUpdate(t *testing.T) {
117117
}
118118

119119
func Test_ServerUpdateCustom(t *testing.T) {
120+
121+
// IP cases.
120122
t.Run("Try to remove ip from server without ip", core.Test(&core.TestConfig{
121123
Commands: GetCommands(),
122124
BeforeFunc: func(ctx *core.BeforeFuncCtx) error {
@@ -184,6 +186,7 @@ func Test_ServerUpdateCustom(t *testing.T) {
184186
},
185187
}))
186188

189+
// Placement group cases.
187190
t.Run("Update server placement-group-id from server with placement-group-id", core.Test(&core.TestConfig{
188191
Commands: GetCommands(),
189192
BeforeFunc: func(ctx *core.BeforeFuncCtx) error {
@@ -209,6 +212,7 @@ func Test_ServerUpdateCustom(t *testing.T) {
209212
},
210213
}))
211214

215+
// Security group cases.
212216
t.Run("Update server security-group-id from server with security-group-id", core.Test(&core.TestConfig{
213217
Commands: GetCommands(),
214218
BeforeFunc: func(ctx *core.BeforeFuncCtx) error {
@@ -233,4 +237,40 @@ func Test_ServerUpdateCustom(t *testing.T) {
233237
return nil
234238
},
235239
}))
240+
241+
// Volumes cases.
242+
t.Run("Volumes", func(t *testing.T) {
243+
t.Run("valid simple block volume", core.Test(&core.TestConfig{
244+
Commands: GetCommands(),
245+
BeforeFunc: func(ctx *core.BeforeFuncCtx) error {
246+
ctx.Meta["Response"] = ctx.ExecuteCmd("scw instance volume create name=cli-test size=10G volume-type=b_ssd")
247+
return createVanillaServer(ctx)
248+
},
249+
Cmd: `scw instance server update server-id={{ .Server.ID }} volume-ids.0={{ (index .Server.Volumes "0").ID }} volume-ids.1={{ .Response.Volume.ID }}`,
250+
Check: func(t *testing.T, ctx *core.CheckFuncCtx) {
251+
require.NoError(t, ctx.Err)
252+
assert.Equal(t, 20*scw.GB, ctx.Result.(*instance.UpdateServerResponse).Server.Volumes["0"].Size)
253+
assert.Equal(t, 10*scw.GB, ctx.Result.(*instance.UpdateServerResponse).Server.Volumes["1"].Size)
254+
},
255+
AfterFunc: deleteVanillaServer,
256+
}))
257+
258+
t.Run("detach all volumes", core.Test(&core.TestConfig{
259+
Commands: GetCommands(),
260+
BeforeFunc: func(ctx *core.BeforeFuncCtx) error {
261+
ctx.Meta["Server"] = ctx.ExecuteCmd("scw instance server create stopped=true image=ubuntu-bionic additional-volumes.0=block:10G")
262+
return nil
263+
},
264+
Cmd: `scw instance server update server-id={{ .Server.ID }} volume-ids=none`,
265+
Check: func(t *testing.T, ctx *core.CheckFuncCtx) {
266+
require.NoError(t, ctx.Err)
267+
assert.Equal(t, 0, len(ctx.Result.(*instance.UpdateServerResponse).Server.Volumes))
268+
},
269+
AfterFunc: func(ctx *core.AfterFuncCtx) error {
270+
ctx.ExecuteCmd(`scw instance delete volume volume-id={{ (index .Server.Volumes "0").ID }}`)
271+
ctx.ExecuteCmd(`scw instance delete volume volume-id={{ (index .Server.Volumes "1").ID }}`)
272+
return deleteVanillaServer(ctx)
273+
},
274+
}))
275+
})
236276
}

0 commit comments

Comments
 (0)