Skip to content

Commit

Permalink
feat(instance_image) : add resource for instance image (#1331)
Browse files Browse the repository at this point in the history
Co-authored-by: Rémy Léone <rleone@scaleway.com>
Co-authored-by: Yacine Fodil <105779815+yfodil@users.noreply.github.com>
Co-authored-by: Jules Castéran <jcasteran@scaleway.com>
  • Loading branch information
4 people committed Jul 8, 2022
1 parent 35b2997 commit bd1d718
Show file tree
Hide file tree
Showing 12 changed files with 16,265 additions and 33 deletions.
132 changes: 132 additions & 0 deletions docs/resources/instance_image.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
page_title: "Scaleway: scaleway_instance_image"
description: |-
Manages Scaleway Instance Images.
---

# scaleway_instance_image

Creates and manages Scaleway Compute Images.
For more information, see [the documentation](https://developers.scaleway.com/en/products/instance/api/#images-41389b).

## Example

### From a volume

```hcl
resource "scaleway_instance_volume" "volume" {
type = "b_ssd"
size_in_gb = 20
}
resource "scaleway_instance_snapshot" "volume_snapshot" {
volume_id = scaleway_instance_volume.volume.id
}
resource "scaleway_instance_image" "volume_image" {
name = "image_from_volume"
root_volume_id = scaleway_instance_snapshot.volume_snapshot.id
}
```

### From a server

```hcl
resource "scaleway_instance_server" "server" {
image = "ubuntu_focal"
type = "DEV1-S"
}
resource "scaleway_instance_snapshot" "server_snapshot" {
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
}
resource "scaleway_instance_image" "server_image" {
name = "image_from_server"
root_volume_id = scaleway_instance_snapshot.server_snapshot.id
}
```

### With additional volumes

```hcl
resource "scaleway_instance_server" "server" {
image = "ubuntu_focal"
type = "DEV1-S"
}
resource "scaleway_instance_volume" "volume" {
type = "b_ssd"
size_in_gb = 20
}
resource "scaleway_instance_snapshot" "volume_snapshot" {
volume_id = scaleway_instance_volume.volume.id
}
resource "scaleway_instance_snapshot" "server_snapshot" {
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
}
resource "scaleway_instance_image" "image" {
name = "image_with_extra_volumes"
root_volume_id = scaleway_instance_snapshot.server_snapshot.id
additional_volumes = [
scaleway_instance_snapshot.volume_snapshot.id
]
}
```

## Arguments Reference

The following arguments are supported:

- `root_volume_id` - (Required) The ID of the snapshot of the volume to be used as root in the image.
- `name` - (Optional) The name of the image. If not provided it will be randomly generated.
- `architecture` - (Optional, default `x86_64`) The architecture the image is compatible with. Possible values are: `x86_64` or `arm`.
- `additional_volume_ids` - (Optional) List of IDs of the snapshots of the additional volumes to be attached to the image.

-> **Important:** For now it is only possible to have 1 additional_volume.

- `tags` - (Optional) A list of tags to apply to the image.
- `public` - (Optional) Set to `true` if the image is public.
- `zone` - (Defaults to provider `zone`) The [zone](../guides/regions_and_zones.md#zones) in which the image should be created.
- `project_id` - (Defaults to provider `project_id`) The ID of the project the image is associated with.

## Attributes Reference

In addition to all above arguments, the following attributes are exported:

- `id` - The ID of the image.
- `creation_date` - Date of the image creation.
- `modification_date` - Date of image latest update.
- `from_server_id` - ID of the server the image is based on (in case it is a backup).
- `state` - State of the image. Possible values are: `available`, `creating` or `error`.
- `organization_id` - The organization ID the image is associated with.
- `additional_volumes` - The description of the extra volumes attached to the image.

-> The `additional_volumes` block contains :
- `id` - The ID of the volume.
- `name` - The name of the volume.
- `export_uri` - The export URI of the volume.
- `size` - The size of the volume.
- `volume_type` - The type of volume, possible values are `l_ssd` and `b_ssd`.
- `creation_date` - Date of the volume creation.
- `modification_date` - Date of volume latest update.
- `organization` - The organization ID the volume is associated with.
- `project` - ID of the project the volume is associated with
- `tags` - List of tags associated with the volume.
- `state` - State of the volume.
- `zone` - The [zone](../guides/regions_and_zones.md#zones) in which the volume is.
- `server` - Description of the server containing the volume (in case the image is a backup from a server).

-> The `server` block contains :
- `id` - ID of the server containing the volume.
- `name` - Name of the server containing the volume.

## Import

Images can be imported using the `{zone}/{id}`, e.g.

```bash
$ terraform import scaleway_instance_image.main fr-par-1/11111111-1111-1111-1111-111111111111
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/terraform-plugin-log v0.4.1
github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0
github.com/robfig/cron/v3 v3.0.1
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220706124610-072bee915715
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220707130308-7ec0786bddf2
github.com/stretchr/testify v1.8.0
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220706124610-072bee915715 h1:e+1PXi8wsIPdl8DUOm+R23C1D91UYph6BJcrJL5NpU0=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220706124610-072bee915715/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220707130308-7ec0786bddf2 h1:whbV5sKu3tYwN11SGjH1jGQb2RxB4gCCeL9KRKfPwmg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220707130308-7ec0786bddf2/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
Expand Down
30 changes: 0 additions & 30 deletions scaleway/data_source_instance_image_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package scaleway

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
)

func TestAccScalewayDataSourceInstanceImage_Basic(t *testing.T) {
Expand Down Expand Up @@ -46,30 +43,3 @@ func TestAccScalewayDataSourceInstanceImage_Basic(t *testing.T) {
},
})
}

func testAccCheckScalewayInstanceImageExists(tt *TestTools, n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]

if !ok {
return fmt.Errorf("not found: %s", n)
}

zone, ID, err := parseZonedID(rs.Primary.ID)
if err != nil {
return err
}

instanceAPI := instance.NewAPI(tt.Meta.scwClient)
_, err = instanceAPI.GetImage(&instance.GetImageRequest{
ImageID: ID,
Zone: zone,
})

if err != nil {
return err
}

return nil
}
}
84 changes: 84 additions & 0 deletions scaleway/helpers_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"sort"
"strconv"
"time"

"github.com/dustin/go-humanize"
Expand Down Expand Up @@ -32,6 +33,8 @@ const (
defaultInstanceRetryInterval = 5 * time.Second

defaultInstanceSnapshotWaitTimeout = 1 * time.Hour

defaultInstanceImageTimeout = 1 * time.Hour
)

// instanceAPIWithZone returns a new instance API and the zone for a Create request
Expand Down Expand Up @@ -477,3 +480,84 @@ func waitForPrivateNIC(ctx context.Context, instanceAPI *instance.API, zone scw.

return nic, err
}

func waitForInstanceImage(ctx context.Context, api *instance.API, zone scw.Zone, id string, timeout time.Duration) (*instance.Image, error) {
retryInterval := defaultInstanceRetryInterval
if DefaultWaitRetryInterval != nil {
retryInterval = *DefaultWaitRetryInterval
}

image, err := api.WaitForImage(&instance.WaitForImageRequest{
ImageID: id,
Zone: zone,
Timeout: scw.TimeDurationPtr(timeout),
RetryInterval: &retryInterval,
}, scw.WithContext(ctx))

return image, err
}

func getSnapshotsFromIds(ctx context.Context, snapIDs []interface{}, instanceAPI *instance.API) ([]*instance.GetSnapshotResponse, error) {
snapResponses := []*instance.GetSnapshotResponse(nil)
for _, snapID := range snapIDs {
zone, id, err := parseZonedID(snapID.(string))
if err != nil {
return nil, err
}
snapshot, err := instanceAPI.GetSnapshot(&instance.GetSnapshotRequest{
Zone: zone,
SnapshotID: id,
}, scw.WithContext(ctx))
if err != nil {
return snapResponses, fmt.Errorf("extra volumes : could not find snapshot with id %s", snapID)
}
snapResponses = append(snapResponses, snapshot)
}
return snapResponses, nil
}

func expandInstanceImageExtraVolumesTemplates(snapshots []*instance.GetSnapshotResponse) map[string]*instance.VolumeTemplate {
volTemplates := map[string]*instance.VolumeTemplate{}
if snapshots == nil {
return volTemplates
}
for i, snapshot := range snapshots {
snap := snapshot.Snapshot
volTemplate := &instance.VolumeTemplate{
ID: snap.ID,
Name: snap.BaseVolume.Name,
Size: snap.Size,
VolumeType: snap.VolumeType,
}
volTemplates[strconv.Itoa(i+1)] = volTemplate
}
return volTemplates
}

func flattenInstanceImageExtraVolumes(volumes map[string]*instance.Volume, zone scw.Zone) interface{} {
volumesFlat := []map[string]interface{}(nil)
for _, volume := range volumes {
server := map[string]interface{}{}
if volume.Server != nil {
server["id"] = volume.Server.ID
server["name"] = volume.Server.Name
}
volumeFlat := map[string]interface{}{
"id": newZonedIDString(zone, volume.ID),
"name": volume.Name,
"export_uri": volume.ExportURI,
"size": volume.Size,
"volume_type": volume.VolumeType,
"creation_date": volume.CreationDate,
"modification_date": volume.ModificationDate,
"organization": volume.Organization,
"project": volume.Project,
"tags": volume.Tags,
"state": volume.State,
"zone": volume.Zone,
"server": server,
}
volumesFlat = append(volumesFlat, volumeFlat)
}
return volumesFlat
}
1 change: 1 addition & 0 deletions scaleway/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
"scaleway_domain_zone": resourceScalewayDomainZone(),
"scaleway_function": resourceScalewayFunction(),
"scaleway_function_namespace": resourceScalewayFunctionNamespace(),
"scaleway_instance_image": resourceScalewayInstanceImage(),
"scaleway_instance_ip": resourceScalewayInstanceIP(),
"scaleway_instance_ip_reverse_dns": resourceScalewayInstanceIPReverseDNS(),
"scaleway_instance_volume": resourceScalewayInstanceVolume(),
Expand Down

0 comments on commit bd1d718

Please sign in to comment.