Skip to content

Commit c89b594

Browse files
authored
feat(instance): brk change, use TypeMap for user_data and remove user… (#738)
1 parent 6d9a5f4 commit c89b594

7 files changed

+3963
-13376
lines changed

docs/guides/migration_guide_v2.md

+47-8
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,49 @@ terraform import $NEW_RESOURCE_NAME $ID
175175

176176
### Instance
177177

178+
#### Breaking changes
179+
180+
##### User-data is a Map and cloud_init field is included in the user-data map
181+
182+
User data are now saved as TypeMap instead of TypeSet.
183+
In particular, the `cloud_init` attribute is now managed as a common field in the user-data with the standard `cloud-init`.
184+
185+
v1.X:
186+
187+
```hcl
188+
resource "scaleway_instance_server" "web" {
189+
type = "DEV1-S"
190+
image = "ubuntu_focal"
191+
192+
user_data {
193+
key = "foo"
194+
value = "bar"
195+
}
196+
197+
cloud_init = file("cloud-init.yml")
198+
}
199+
```
200+
201+
v2.X:
202+
203+
```hcl
204+
resource "scaleway_instance_server" "web" {
205+
type = "DEV1-S"
206+
image = "ubuntu_focal"
207+
208+
user_data {
209+
foo = "bar"
210+
cloud-init = file("cloud-init.yml")
211+
}
212+
}
213+
```
214+
215+
#### Renaming
216+
178217
All the old instance resources have been regrouped under a new name: `Instance`.
179218
This means that all old instance resources are now prefixed with `scaleway_instance_`.
180219

181-
#### Renamed: `scaleway_server` -> `scaleway_instance_server`
220+
##### Renamed: `scaleway_server` -> `scaleway_instance_server`
182221

183222
`scaleway_server` was renamed to `scaleway_instance_server`.
184223

@@ -206,7 +245,7 @@ resource "scaleway_instance_server" "web" {
206245
}
207246
```
208247

209-
#### Renamed: `scaleway_ip` -> `scaleway_instance_ip`
248+
##### Renamed: `scaleway_ip` -> `scaleway_instance_ip`
210249

211250
`scaleway_ip` was renamed to `scaleway_instance_ip` and the `server` attribute, used to attach an IP has been moved to `scaleway_instance_server.id_id`
212251

@@ -215,35 +254,35 @@ resource "scaleway_instance_ip" "test_ip" {
215254
}
216255
```
217256

218-
#### Renamed: `scaleway_volume` -> `scaleway_instance_volume`
257+
##### Renamed: `scaleway_volume` -> `scaleway_instance_volume`
219258

220259
`scaleway_volume` was renamed to `scaleway_instance_volume`.
221260
The former attributes can still be used on the new volume resource.
222261

223262
Additionally, from now on, you can also create new volumes based on other volumes or snapshots.
224263
For more information check the [new volume `scaleway_instance_volume` resource](../resources/instance_volume.md).
225264

226-
#### Renamed: `scaleway_ssh_key` -> `scaleway_account_ssk_key`
265+
##### Renamed: `scaleway_ssh_key` -> `scaleway_account_ssk_key`
227266

228267
`scaleway_ssh_key` was renamed to `scaleway_account_ssk_key`
229268
The `key` attribute has been renamed to `public_key`.
230269
A `name` required attribute and an `organization_id` optional attribute have been added.
231270

232-
#### Removed: `scaleway_user_data`
271+
##### Removed: `scaleway_user_data`
233272

234273
`scaleway_user_data` is now part of the `scaleway_instance_server` resource.
235274

236-
#### Removed: `scaleway_token`
275+
##### Removed: `scaleway_token`
237276

238277
The `scaleway_token` was removed in version 2.
239278

240279
Tokens should be created in the console.
241280

242-
#### Renamed: `scaleway_ip_reverse_dns` -> `scaleway_instance_ip_reverse_dns`
281+
##### Renamed: `scaleway_ip_reverse_dns` -> `scaleway_instance_ip_reverse_dns`
243282

244283
`scaleway_ip_reverse_dns` was renamed to `scaleway_instance_ip_reverse_dns`.
245284

246-
#### Removed: `scaleway_volume_attachment`
285+
##### Removed: `scaleway_volume_attachment`
247286

248287
The `scaleway_volume_attachment` was removed in version 2.
249288

docs/resources/instance_server.md

+8-18
Original file line numberDiff line numberDiff line change
@@ -100,22 +100,13 @@ resource "scaleway_instance_server" "web" {
100100

101101
```hcl
102102
resource "scaleway_instance_server" "web" {
103-
type = "DEV1-S"
103+
type = "DEV1-S"
104104
image = "ubuntu_focal"
105105
106-
tags = [ "web", "public" ]
107-
108-
user_data {
109-
key = "plop"
110-
value = "world"
111-
}
112-
113106
user_data {
114-
key = "xavier"
115-
value = "niel"
107+
foo = "bar"
108+
cloud-init = file("${path.module}/cloud-init.yml")
116109
}
117-
118-
cloud_init = file("${path.module}/cloud-init.yml")
119110
}
120111
```
121112

@@ -169,13 +160,12 @@ attached to the server. Updates to this field will trigger a stop/start of the s
169160

170161
- `state` - (Defaults to `started`) The state of the server. Possible values are: `started`, `stopped` or `standby`.
171162

172-
- `cloud_init` - (Optional) The cloud init script associated with this server. Updates to this field will trigger a stop/start of the server.
173-
174163
- `user_data` - (Optional) The user data associated with the server.
175-
176-
- `key` - (Required) The user data key. The `cloud-init` key is reserved, please use `cloud_init` attribute instead.
177-
178-
- `value` - (Required) The user data content. It could be a string or a file content using [file](https://www.terraform.io/docs/configuration/functions/file.html) or [filebase64](https://www.terraform.io/docs/configuration/functions/filebase64.html) for example.
164+
Use the `cloud-init` key to use [cloud-init](https://cloudinit.readthedocs.io/en/latest/) on your instance.
165+
You can define values using:
166+
- string
167+
- UTF-8 encoded file content using [file](https://www.terraform.io/docs/configuration/functions/file.html)
168+
- Binary files using [filebase64](https://www.terraform.io/docs/configuration/functions/filebase64.html).
179169

180170
- `boot_type` - The boot Type of the server. Possible values are: `local`, `bootscript` or `rescue`.
181171

scaleway/helpers_instance.go

-23
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package scaleway
33
import (
44
"context"
55
"fmt"
6-
"hash/crc32"
76
"sort"
87
"time"
98

@@ -62,28 +61,6 @@ func instanceAPIWithZoneAndNestedID(m interface{}, zonedNestedID string) (*insta
6261
return instanceAPI, zone, innerID, outerID, nil
6362
}
6463

65-
// hash hashes a string to a unique hashcode.
66-
//
67-
// crc32 returns a uint32, but for our use we need
68-
// and non negative integer. Here we cast to an integer
69-
// and invert it if the result is negative.
70-
func hash(s string) int {
71-
v := int(crc32.ChecksumIEEE([]byte(s)))
72-
if v >= 0 {
73-
return v
74-
}
75-
if -v >= 0 {
76-
return -v
77-
}
78-
// v == MinInt
79-
return 0
80-
}
81-
82-
func userDataHash(v interface{}) int {
83-
userData := v.(map[string]interface{})
84-
return hash(userData["key"].(string) + userData["value"].(string))
85-
}
86-
8764
// orderVolumes return an ordered slice based on the volume map key "0", "1", "2",...
8865
func orderVolumes(v map[string]*instance.Volume) []*instance.Volume {
8966
indexes := []string{}

scaleway/resource_instance_server.go

+20-46
Original file line numberDiff line numberDiff line change
@@ -195,26 +195,11 @@ func resourceScalewayInstanceServer() *schema.Resource {
195195
ValidateFunc: validation.StringLenBetween(0, 127998),
196196
},
197197
"user_data": {
198-
Type: schema.TypeSet,
198+
Type: schema.TypeMap,
199199
Optional: true,
200-
MaxItems: 98,
201200
Description: "The user data associated with the server", // TODO: document reserved keys (`cloud-init`)
202-
Set: userDataHash,
203-
Elem: &schema.Resource{
204-
Schema: map[string]*schema.Schema{
205-
"key": {
206-
Type: schema.TypeString,
207-
Required: true,
208-
ValidateFunc: validationStringNotInSlice([]string{"cloud-init"}, true),
209-
Description: "A user data key, the value \"cloud-init\" is not allowed",
210-
},
211-
"value": {
212-
Type: schema.TypeString,
213-
Required: true,
214-
ValidateFunc: validation.StringLenBetween(0, 127998),
215-
Description: "A user value",
216-
},
217-
},
201+
Elem: &schema.Schema{
202+
Type: schema.TypeString,
218203
},
219204
},
220205
"zone": zoneSchema(),
@@ -340,11 +325,9 @@ func resourceScalewayInstanceServerCreate(ctx context.Context, d *schema.Resourc
340325
UserData: make(map[string]io.Reader),
341326
}
342327

343-
if allUserData, ok := d.GetOk("user_data"); ok {
344-
userDataSet := allUserData.(*schema.Set)
345-
for _, rawUserData := range userDataSet.List() {
346-
userData := rawUserData.(map[string]interface{})
347-
userDataRequests.UserData[userData["key"].(string)] = bytes.NewBufferString(userData["value"].(string))
328+
if rawUserData, ok := d.GetOk("user_data"); ok {
329+
for key, value := range rawUserData.(map[string]interface{}) {
330+
userDataRequests.UserData[key] = bytes.NewBufferString(value.(string))
348331
}
349332
}
350333

@@ -487,23 +470,20 @@ func resourceScalewayInstanceServerRead(ctx context.Context, d *schema.ResourceD
487470
ServerID: ID,
488471
}, scw.WithContext(ctx))
489472

490-
var userDataList []interface{}
473+
userData := make(map[string]interface{})
491474
for key, value := range allUserData.UserData {
492-
userData, err := ioutil.ReadAll(value)
475+
userDataValue, err := ioutil.ReadAll(value)
493476
if err != nil {
494477
return diag.FromErr(err)
495478
}
496-
if key != "cloud-init" {
497-
userDataList = append(userDataList, map[string]interface{}{
498-
"key": key,
499-
"value": string(userData),
500-
})
501-
} else {
502-
_ = d.Set("cloud_init", string(userData))
503-
}
479+
//if key != "cloud-init" {
480+
userData[key] = string(userDataValue)
481+
// } else {
482+
//_ = d.Set("cloud_init", string(userDataValue))
483+
//}
504484
}
505-
if len(userDataList) > 0 {
506-
_ = d.Set("user_data", schema.NewSet(userDataHash, userDataList))
485+
if len(userData) > 0 {
486+
_ = d.Set("user_data", userData)
507487
}
508488

509489
return nil
@@ -661,25 +641,19 @@ func resourceScalewayInstanceServerUpdate(ctx context.Context, d *schema.Resourc
661641
////
662642
// Update server user data
663643
////
664-
if d.HasChanges("cloud_init", "user_data") {
644+
if d.HasChanges("user_data") {
665645
userDataRequests := &instance.SetAllServerUserDataRequest{
666646
Zone: zone,
667647
ServerID: ID,
668648
UserData: make(map[string]io.Reader),
669649
}
670650

671651
if allUserData, ok := d.GetOk("user_data"); ok {
672-
userDataSet := allUserData.(*schema.Set)
673-
for _, rawUserData := range userDataSet.List() {
674-
userData := rawUserData.(map[string]interface{})
675-
userDataRequests.UserData[userData["key"].(string)] = bytes.NewBufferString(userData["value"].(string))
652+
userDataMap := allUserData.(map[string]interface{})
653+
for key, value := range userDataMap {
654+
userDataRequests.UserData[key] = bytes.NewBufferString(value.(string))
676655
}
677-
}
678-
679-
// cloud init script is set in user data
680-
if cloudInit, ok := d.GetOk("cloud_init"); ok {
681-
userDataRequests.UserData["cloud-init"] = bytes.NewBufferString(cloudInit.(string))
682-
if !isStopped {
656+
if !isStopped && d.HasChange("user_data.cloud-init") {
683657
warnings = append(warnings, diag.Diagnostic{
684658
Severity: diag.Warning,
685659
Summary: "instance may need to be rebooted to use the new cloud init config",

0 commit comments

Comments
 (0)