Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(instance): add image update command #2539

Merged
merged 5 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲
🟥🟥🟥 STDERR️️ 🟥🟥🟥️
Update properties of an instance image.

USAGE:
scw instance image update [arg=value ...]

EXAMPLES:
Update image name
scw instance image update image-id=11111111-1111-1111-1111-111111111111 name=foo

Update image public
scw instance image update image-id=11111111-1111-1111-1111-111111111111 public=true

Add extra volume
scw instance image update image-id=11111111-1111-1111-1111-111111111111 extra-volumes.1.id=11111111-1111-1111-1111-111111111111

ARGS:
image-id
[name]
[arch] (x86_64 | arm)
[extra-volumes.{index}.id] Additional extra-volume ID
[from-server]
[public]
[tags.{index}]
[project] Project ID to use. If none is passed the default project ID will be used
[organization] Organization ID to use. If none is passed the default organization ID will be used
[zone=fr-par-1] Zone to target. If none is passed will use default zone from the config (fr-par-1 | fr-par-2 | fr-par-3 | nl-ams-1 | nl-ams-2 | pl-waw-1)

FLAGS:
-h, --help help for update

GLOBAL FLAGS:
-c, --config string The path to the config file
-D, --debug Enable debug mode
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human")
-p, --profile string The config profile to use
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ AVAILABLE COMMANDS:
delete Delete an instance image
get Get an instance image
list List instance images
update Update an instance image
wait Wait for image to reach a stable state

FLAGS:
Expand Down
49 changes: 49 additions & 0 deletions docs/commands/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Instance API
- [Delete an instance image](#delete-an-instance-image)
- [Get an instance image](#get-an-instance-image)
- [List instance images](#list-instance-images)
- [Update an instance image](#update-an-instance-image)
- [Wait for image to reach a stable state](#wait-for-image-to-reach-a-stable-state)
- [IP management commands](#ip-management-commands)
- [Attach an IP to a given server](#attach-an-ip-to-a-given-server)
Expand Down Expand Up @@ -235,6 +236,54 @@ scw instance image list



### Update an instance image

Update properties of an instance image.

**Usage:**

```
scw instance image update [arg=value ...]
```


**Args:**

| Name | | Description |
|------|---|-------------|
| image-id | Required | |
| name | | |
| arch | One of: `x86_64`, `arm` | |
| extra-volumes.{index}.id | | Additional extra-volume ID |
| from-server | | |
| public | | |
| tags.{index} | | |
| project | | Project ID to use. If none is passed the default project ID will be used |
| organization | | Organization ID to use. If none is passed the default organization ID will be used |
| zone | Default: `fr-par-1`<br />One of: `fr-par-1`, `fr-par-2`, `fr-par-3`, `nl-ams-1`, `nl-ams-2`, `pl-waw-1` | Zone to target. If none is passed will use default zone from the config |


**Examples:**


Update image name
```
scw instance image update image-id=11111111-1111-1111-1111-111111111111 name=foo
```

Update image public
```
scw instance image update image-id=11111111-1111-1111-1111-111111111111 public=true
```

Add extra volume
```
scw instance image update image-id=11111111-1111-1111-1111-111111111111 extra-volumes.1.id=11111111-1111-1111-1111-111111111111
```




### Wait for image to reach a stable state

Wait for image to reach a stable state. This is similar to using --wait flag on other action commands, but without requiring a new action on the image.
Expand Down
1 change: 1 addition & 0 deletions internal/namespaces/instance/v1/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func GetCommands() *core.Commands {
cmds.MustFind("instance", "image", "delete").Override(imageDeleteBuilder)
cmds.Merge(core.NewCommands(
imageWaitCommand(),
imageUpdateCommand(),
))

//
Expand Down
120 changes: 120 additions & 0 deletions internal/namespaces/instance/v1/custom_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/scaleway/scaleway-cli/v2/internal/core"
"github.com/scaleway/scaleway-cli/v2/internal/human"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
"github.com/scaleway/scaleway-sdk-go/logger"
"github.com/scaleway/scaleway-sdk-go/scw"
)

Expand Down Expand Up @@ -314,6 +315,125 @@ func imageDeleteBuilder(c *core.Command) *core.Command {
return c
}

//
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it not generated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the UpdateImage method has been hand coded in the SDK

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we check with the instance team if the update image could be generated now? Maybe the reasons of why it was hardcoded are no long true and it could be generated just like any other function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Commands
//

func imageUpdateCommand() *core.Command {
return &core.Command{
Short: `Update an instance image`,
Long: `Update properties of an instance image.`,
Namespace: "instance",
Resource: "image",
Verb: "update",
ArgsType: reflect.TypeOf(instance.UpdateImageRequest{}),
ArgSpecs: core.ArgSpecs{
{
Name: "image-id",
Required: true,
Positional: false,
},
{
Name: "name",
Required: false,
Positional: false,
},
{
Name: "arch",
Required: false,
Positional: false,
EnumValues: []string{"x86_64", "arm"},
},
{
Name: "extra-volumes.{index}.id",
Short: `Additional extra-volume ID`,
Required: false,
Deprecated: false,
Positional: false,
},
{
Name: "from-server",
Required: false,
Positional: false,
},
{
Name: "public",
Required: false,
Positional: false,
},
{
Name: "tags.{index}",
Required: false,
Positional: false,
},
core.ProjectArgSpec(),
core.OrganizationArgSpec(),
core.ZoneArgSpec(scw.ZoneFrPar1, scw.ZoneFrPar2, scw.ZoneFrPar3, scw.ZoneNlAms1, scw.ZoneNlAms2, scw.ZonePlWaw1),
},
Run: func(ctx context.Context, argsI interface{}) (i interface{}, err error) {
request := argsI.(*instance.UpdateImageRequest)

client := core.ExtractClient(ctx)
api := instance.NewAPI(client)

getImageResponse, err := api.GetImage(&instance.GetImageRequest{
Zone: request.Zone,
ImageID: request.ImageID,
})
if err != nil {
logger.Warningf("cannot get image %s: %s", request.Name, err)
}

if request.Name == nil {
request.Name = &getImageResponse.Image.Name
}
if request.Arch == "" {
request.Arch = getImageResponse.Image.Arch
}
if request.CreationDate == nil {
request.CreationDate = getImageResponse.Image.CreationDate
}
if request.ModificationDate == nil {
request.ModificationDate = getImageResponse.Image.ModificationDate
}
if request.ExtraVolumes == nil {
request.ExtraVolumes = make(map[string]*instance.VolumeTemplate)
for k, v := range getImageResponse.Image.ExtraVolumes {
volume := instance.VolumeTemplate{
ID: v.ID,
Name: v.Name,
Size: v.Size,
VolumeType: v.VolumeType,
}
request.ExtraVolumes[k] = &volume
}
}
if request.RootVolume == nil {
request.RootVolume = getImageResponse.Image.RootVolume
}
if !request.Public && !getImageResponse.Image.Public {
request.Public = getImageResponse.Image.Public
}

return api.UpdateImage(request)
},
Examples: []*core.Example{
{
Short: "Update image name",
Raw: "scw instance image update image-id=11111111-1111-1111-1111-111111111111 name=foo",
},
{
Short: "Update image public",
Raw: "scw instance image update image-id=11111111-1111-1111-1111-111111111111 public=true",
},
{
Short: "Add extra volume",
Raw: "scw instance image update image-id=11111111-1111-1111-1111-111111111111 extra-volumes.1.id=11111111-1111-1111-1111-111111111111",
},
},
}
}

func imageWaitCommand() *core.Command {
return &core.Command{
Short: `Wait for image to reach a stable state`,
Expand Down
58 changes: 58 additions & 0 deletions internal/namespaces/instance/v1/custom_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,61 @@ func Test_ImageList(t *testing.T) {
AfterFunc: deleteImage("Image"),
}))
}

func Test_ImageUpdate(t *testing.T) {
t.Run("Change name", core.Test(&core.TestConfig{
BeforeFunc: createImage("ImageName"),
Commands: GetCommands(),
Cmd: "scw instance image update image-id={{ .ImageName.Image.ID }} name=foo",
Check: core.TestCheckCombine(
func(t *testing.T, ctx *core.CheckFuncCtx) {
assert.Equal(t, "foo", ctx.Result.(*instance.UpdateImageResponse).Image.Name)
},
core.TestCheckGolden(),
core.TestCheckExitCode(0),
),
AfterFunc: core.AfterFuncCombine(
deleteServer("Server"),
deleteImage("ImageName"),
),
}))

t.Run("Change public from default false to true", core.Test(&core.TestConfig{
BeforeFunc: createImage("ImagePub"),
Commands: GetCommands(),
Cmd: "scw instance image update image-id={{ .ImagePub.Image.ID }} public=true",
Check: core.TestCheckCombine(
func(t *testing.T, ctx *core.CheckFuncCtx) {
assert.Equal(t, true, ctx.Result.(*instance.UpdateImageResponse).Image.Public)
},
core.TestCheckGolden(),
core.TestCheckExitCode(0),
),
AfterFunc: core.AfterFuncCombine(
deleteServer("Server"),
deleteImage("ImagePub"),
),
}))

t.Run("Add extra volume", core.Test(&core.TestConfig{
BeforeFunc: core.BeforeFuncCombine(
createVolume("Volume", 20, instance.VolumeVolumeTypeBSSD),
core.ExecStoreBeforeCmd("SnapshotVol", `scw instance snapshot create -w name=snapVol volume-id={{ .Volume.ID }}`),
createImage("ImageExtraVol"),
),
Commands: GetCommands(),
Cmd: "scw instance image update image-id={{ .ImageExtraVol.Image.ID }} extra-volumes.1.id={{ .SnapshotVol.ID }}",
Check: core.TestCheckCombine(
func(t *testing.T, ctx *core.CheckFuncCtx) {
assert.Equal(t, "snapVol", ctx.Result.(*instance.UpdateImageResponse).Image.ExtraVolumes["1"].Name)
},
core.TestCheckGolden(),
core.TestCheckExitCode(0),
),
AfterFunc: core.AfterFuncCombine(
deleteServer("Server"),
deleteImage("ImageExtraVol"),
deleteVolume("Volume"),
),
}))
}
Loading