Skip to content

Commit bd1d718

Browse files
Mia-CrossremyleoneyfodilCodelax
authored
feat(instance_image) : add resource for instance image (#1331)
Co-authored-by: Rémy Léone <[email protected]> Co-authored-by: Yacine Fodil <[email protected]> Co-authored-by: Jules Castéran <[email protected]>
1 parent 35b2997 commit bd1d718

12 files changed

+16265
-33
lines changed

docs/resources/instance_image.md

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
page_title: "Scaleway: scaleway_instance_image"
3+
description: |-
4+
Manages Scaleway Instance Images.
5+
---
6+
7+
# scaleway_instance_image
8+
9+
Creates and manages Scaleway Compute Images.
10+
For more information, see [the documentation](https://developers.scaleway.com/en/products/instance/api/#images-41389b).
11+
12+
## Example
13+
14+
### From a volume
15+
16+
```hcl
17+
resource "scaleway_instance_volume" "volume" {
18+
type = "b_ssd"
19+
size_in_gb = 20
20+
}
21+
22+
resource "scaleway_instance_snapshot" "volume_snapshot" {
23+
volume_id = scaleway_instance_volume.volume.id
24+
}
25+
26+
resource "scaleway_instance_image" "volume_image" {
27+
name = "image_from_volume"
28+
root_volume_id = scaleway_instance_snapshot.volume_snapshot.id
29+
}
30+
```
31+
32+
### From a server
33+
34+
```hcl
35+
resource "scaleway_instance_server" "server" {
36+
image = "ubuntu_focal"
37+
type = "DEV1-S"
38+
}
39+
40+
resource "scaleway_instance_snapshot" "server_snapshot" {
41+
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
42+
}
43+
44+
resource "scaleway_instance_image" "server_image" {
45+
name = "image_from_server"
46+
root_volume_id = scaleway_instance_snapshot.server_snapshot.id
47+
}
48+
```
49+
50+
### With additional volumes
51+
52+
```hcl
53+
resource "scaleway_instance_server" "server" {
54+
image = "ubuntu_focal"
55+
type = "DEV1-S"
56+
}
57+
58+
resource "scaleway_instance_volume" "volume" {
59+
type = "b_ssd"
60+
size_in_gb = 20
61+
}
62+
63+
resource "scaleway_instance_snapshot" "volume_snapshot" {
64+
volume_id = scaleway_instance_volume.volume.id
65+
}
66+
resource "scaleway_instance_snapshot" "server_snapshot" {
67+
volume_id = scaleway_instance_server.main.root_volume.0.volume_id
68+
}
69+
70+
resource "scaleway_instance_image" "image" {
71+
name = "image_with_extra_volumes"
72+
root_volume_id = scaleway_instance_snapshot.server_snapshot.id
73+
additional_volumes = [
74+
scaleway_instance_snapshot.volume_snapshot.id
75+
]
76+
}
77+
```
78+
79+
## Arguments Reference
80+
81+
The following arguments are supported:
82+
83+
- `root_volume_id` - (Required) The ID of the snapshot of the volume to be used as root in the image.
84+
- `name` - (Optional) The name of the image. If not provided it will be randomly generated.
85+
- `architecture` - (Optional, default `x86_64`) The architecture the image is compatible with. Possible values are: `x86_64` or `arm`.
86+
- `additional_volume_ids` - (Optional) List of IDs of the snapshots of the additional volumes to be attached to the image.
87+
88+
-> **Important:** For now it is only possible to have 1 additional_volume.
89+
90+
- `tags` - (Optional) A list of tags to apply to the image.
91+
- `public` - (Optional) Set to `true` if the image is public.
92+
- `zone` - (Defaults to provider `zone`) The [zone](../guides/regions_and_zones.md#zones) in which the image should be created.
93+
- `project_id` - (Defaults to provider `project_id`) The ID of the project the image is associated with.
94+
95+
## Attributes Reference
96+
97+
In addition to all above arguments, the following attributes are exported:
98+
99+
- `id` - The ID of the image.
100+
- `creation_date` - Date of the image creation.
101+
- `modification_date` - Date of image latest update.
102+
- `from_server_id` - ID of the server the image is based on (in case it is a backup).
103+
- `state` - State of the image. Possible values are: `available`, `creating` or `error`.
104+
- `organization_id` - The organization ID the image is associated with.
105+
- `additional_volumes` - The description of the extra volumes attached to the image.
106+
107+
-> The `additional_volumes` block contains :
108+
- `id` - The ID of the volume.
109+
- `name` - The name of the volume.
110+
- `export_uri` - The export URI of the volume.
111+
- `size` - The size of the volume.
112+
- `volume_type` - The type of volume, possible values are `l_ssd` and `b_ssd`.
113+
- `creation_date` - Date of the volume creation.
114+
- `modification_date` - Date of volume latest update.
115+
- `organization` - The organization ID the volume is associated with.
116+
- `project` - ID of the project the volume is associated with
117+
- `tags` - List of tags associated with the volume.
118+
- `state` - State of the volume.
119+
- `zone` - The [zone](../guides/regions_and_zones.md#zones) in which the volume is.
120+
- `server` - Description of the server containing the volume (in case the image is a backup from a server).
121+
122+
-> The `server` block contains :
123+
- `id` - ID of the server containing the volume.
124+
- `name` - Name of the server containing the volume.
125+
126+
## Import
127+
128+
Images can be imported using the `{zone}/{id}`, e.g.
129+
130+
```bash
131+
$ terraform import scaleway_instance_image.main fr-par-1/11111111-1111-1111-1111-111111111111
132+
```

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/hashicorp/terraform-plugin-log v0.4.1
1313
github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0
1414
github.com/robfig/cron/v3 v3.0.1
15-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220706124610-072bee915715
15+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220707130308-7ec0786bddf2
1616
github.com/stretchr/testify v1.8.0
1717
)
1818

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
254254
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
255255
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
256256
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
257-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220706124610-072bee915715 h1:e+1PXi8wsIPdl8DUOm+R23C1D91UYph6BJcrJL5NpU0=
258-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220706124610-072bee915715/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
257+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220707130308-7ec0786bddf2 h1:whbV5sKu3tYwN11SGjH1jGQb2RxB4gCCeL9KRKfPwmg=
258+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220707130308-7ec0786bddf2/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
259259
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
260260
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
261261
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=

scaleway/data_source_instance_image_test.go

-30
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package scaleway
22

33
import (
4-
"fmt"
54
"testing"
65

76
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
8-
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
9-
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
107
)
118

129
func TestAccScalewayDataSourceInstanceImage_Basic(t *testing.T) {
@@ -46,30 +43,3 @@ func TestAccScalewayDataSourceInstanceImage_Basic(t *testing.T) {
4643
},
4744
})
4845
}
49-
50-
func testAccCheckScalewayInstanceImageExists(tt *TestTools, n string) resource.TestCheckFunc {
51-
return func(s *terraform.State) error {
52-
rs, ok := s.RootModule().Resources[n]
53-
54-
if !ok {
55-
return fmt.Errorf("not found: %s", n)
56-
}
57-
58-
zone, ID, err := parseZonedID(rs.Primary.ID)
59-
if err != nil {
60-
return err
61-
}
62-
63-
instanceAPI := instance.NewAPI(tt.Meta.scwClient)
64-
_, err = instanceAPI.GetImage(&instance.GetImageRequest{
65-
ImageID: ID,
66-
Zone: zone,
67-
})
68-
69-
if err != nil {
70-
return err
71-
}
72-
73-
return nil
74-
}
75-
}

scaleway/helpers_instance.go

+84
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"sort"
7+
"strconv"
78
"time"
89

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

3435
defaultInstanceSnapshotWaitTimeout = 1 * time.Hour
36+
37+
defaultInstanceImageTimeout = 1 * time.Hour
3538
)
3639

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

478481
return nic, err
479482
}
483+
484+
func waitForInstanceImage(ctx context.Context, api *instance.API, zone scw.Zone, id string, timeout time.Duration) (*instance.Image, error) {
485+
retryInterval := defaultInstanceRetryInterval
486+
if DefaultWaitRetryInterval != nil {
487+
retryInterval = *DefaultWaitRetryInterval
488+
}
489+
490+
image, err := api.WaitForImage(&instance.WaitForImageRequest{
491+
ImageID: id,
492+
Zone: zone,
493+
Timeout: scw.TimeDurationPtr(timeout),
494+
RetryInterval: &retryInterval,
495+
}, scw.WithContext(ctx))
496+
497+
return image, err
498+
}
499+
500+
func getSnapshotsFromIds(ctx context.Context, snapIDs []interface{}, instanceAPI *instance.API) ([]*instance.GetSnapshotResponse, error) {
501+
snapResponses := []*instance.GetSnapshotResponse(nil)
502+
for _, snapID := range snapIDs {
503+
zone, id, err := parseZonedID(snapID.(string))
504+
if err != nil {
505+
return nil, err
506+
}
507+
snapshot, err := instanceAPI.GetSnapshot(&instance.GetSnapshotRequest{
508+
Zone: zone,
509+
SnapshotID: id,
510+
}, scw.WithContext(ctx))
511+
if err != nil {
512+
return snapResponses, fmt.Errorf("extra volumes : could not find snapshot with id %s", snapID)
513+
}
514+
snapResponses = append(snapResponses, snapshot)
515+
}
516+
return snapResponses, nil
517+
}
518+
519+
func expandInstanceImageExtraVolumesTemplates(snapshots []*instance.GetSnapshotResponse) map[string]*instance.VolumeTemplate {
520+
volTemplates := map[string]*instance.VolumeTemplate{}
521+
if snapshots == nil {
522+
return volTemplates
523+
}
524+
for i, snapshot := range snapshots {
525+
snap := snapshot.Snapshot
526+
volTemplate := &instance.VolumeTemplate{
527+
ID: snap.ID,
528+
Name: snap.BaseVolume.Name,
529+
Size: snap.Size,
530+
VolumeType: snap.VolumeType,
531+
}
532+
volTemplates[strconv.Itoa(i+1)] = volTemplate
533+
}
534+
return volTemplates
535+
}
536+
537+
func flattenInstanceImageExtraVolumes(volumes map[string]*instance.Volume, zone scw.Zone) interface{} {
538+
volumesFlat := []map[string]interface{}(nil)
539+
for _, volume := range volumes {
540+
server := map[string]interface{}{}
541+
if volume.Server != nil {
542+
server["id"] = volume.Server.ID
543+
server["name"] = volume.Server.Name
544+
}
545+
volumeFlat := map[string]interface{}{
546+
"id": newZonedIDString(zone, volume.ID),
547+
"name": volume.Name,
548+
"export_uri": volume.ExportURI,
549+
"size": volume.Size,
550+
"volume_type": volume.VolumeType,
551+
"creation_date": volume.CreationDate,
552+
"modification_date": volume.ModificationDate,
553+
"organization": volume.Organization,
554+
"project": volume.Project,
555+
"tags": volume.Tags,
556+
"state": volume.State,
557+
"zone": volume.Zone,
558+
"server": server,
559+
}
560+
volumesFlat = append(volumesFlat, volumeFlat)
561+
}
562+
return volumesFlat
563+
}

scaleway/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
9898
"scaleway_domain_zone": resourceScalewayDomainZone(),
9999
"scaleway_function": resourceScalewayFunction(),
100100
"scaleway_function_namespace": resourceScalewayFunctionNamespace(),
101+
"scaleway_instance_image": resourceScalewayInstanceImage(),
101102
"scaleway_instance_ip": resourceScalewayInstanceIP(),
102103
"scaleway_instance_ip_reverse_dns": resourceScalewayInstanceIPReverseDNS(),
103104
"scaleway_instance_volume": resourceScalewayInstanceVolume(),

0 commit comments

Comments
 (0)