Skip to content

Commit d1022fb

Browse files
authored
feat(baremetal): support easy partitioning (#3010)
* feat(baremetal): support easy partitioning * rename datasource * feat(baremetal): add check compatible * move test to the right package and add data source to provier * add tests * register tests easy-partitioning * implement logic * issue with partition * fix tests and add more * tests and golangci * add documentation and checkdestroy * fix review * fix and refacto after review * add tests and fix edge case * fix golangci-lint * fix linter
1 parent 4e9a3d5 commit d1022fb

15 files changed

+27106
-2
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
subcategory: "Elastic Metal"
3+
page_title: "Scaleway: scaleway_baremetal_easy_partitioning"
4+
---
5+
6+
# scaleway_baremetal_easy_partitioning
7+
8+
The scaleway_easy_partitioning data source allows you to retrieve a ready-to-use partitioning schema for a BareMetal server. This schema can be used for custom installations with optional swap and extra partitions.
9+
10+
This data source simplifies the process of generating valid partitioning configurations, especially useful when dealing with OS and offer compatibility requirements.
11+
12+
## Example Usage
13+
14+
```hcl
15+
data "scaleway_easy_partitioning" "default" {
16+
offer_id = "11111111-1111-1111-1111-111111111111"
17+
os_id = "22222222-2222-2222-2222-222222222222"
18+
swap = true
19+
extra_partition = true
20+
ext_4_mountpoint = "/data"
21+
}
22+
```
23+
24+
```hcl
25+
data "scaleway_baremetal_offer" "my_offer" {
26+
zone = "fr-par-1"
27+
name = "EM-B220E-NVME"
28+
}
29+
30+
data "scaleway_baremetal_os" "my_os" {
31+
zone = "fr-par-1"
32+
name = "Ubuntu"
33+
version = "22.04 LTS (Jammy Jellyfish)"
34+
}
35+
36+
resource "scaleway_iam_ssh_key" "main" {
37+
name = "my-ssh-key"
38+
public_key = "my-ssh-key-public"
39+
}
40+
41+
data "scaleway_baremetal_easy_partitioning" "test" {
42+
offer_id = data.scaleway_baremetal_offer.my_offer.offer_id
43+
os_id = data.scaleway_baremetal_os.my_os.os_id
44+
swap = false
45+
ext_4_mountpoint = "/hello"
46+
}
47+
48+
resource "scaleway_baremetal_server" "base" {
49+
name = "my-baremetal-server"
50+
zone = "fr-par-1"
51+
description = "test a description"
52+
offer = data.scaleway_baremetal_offer.my_offer.offer_id
53+
os = data.scaleway_baremetal_os.my_os.os_id
54+
partitioning = data.scaleway_baremetal_easy_partitioning.test.json_partition
55+
tags = ["terraform-test", "scaleway_baremetal_server", "minimal", "edited"]
56+
ssh_key_ids = [scaleway_iam_ssh_key.main.id]
57+
}
58+
```
59+
60+
## Argument Reference
61+
62+
- `offer_id` (Required) The UUID of the BareMetal offer.
63+
64+
- `os_id` (Required) The UUID of the OS image.
65+
66+
- `swap` (Optional, Default: true) Whether to include a swap partition.
67+
68+
- `extra_partition` (Optional, Default: true) Whether to add an extra ext4 data partition.
69+
70+
- `ext_4_mountpoint` (Optional, Default: "/data") The mount point for the extra partition. Must be an absolute path using alphanumeric characters and underscores.
71+
72+
## Attributes Reference
73+
74+
In addition to all above arguments, the following attributes are exported:
75+
76+
- `id` — A composite identifier derived from offer_id and os_id.
77+
78+
- `json_partition` — A validated partitioning schema in JSON format that can be directly used for BareMetal server deployment.

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ func Provider(config *Config) plugin.ProviderFunc {
247247
"scaleway_account_ssh_key": iam.DataSourceSSHKey(),
248248
"scaleway_availability_zones": az.DataSourceAvailabilityZones(),
249249
"scaleway_baremetal_offer": baremetal.DataSourceOffer(),
250+
"scaleway_baremetal_easy_partitioning": baremetal.DataEasyPartitioning(),
250251
"scaleway_baremetal_option": baremetal.DataSourceOption(),
251252
"scaleway_baremetal_os": baremetal.DataSourceOS(),
252253
"scaleway_baremetal_server": baremetal.DataSourceServer(),
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
package baremetal
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/scaleway/scaleway-sdk-go/api/baremetal/v1"
12+
"github.com/scaleway/scaleway-sdk-go/scw"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/dsf"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/zonal"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
16+
)
17+
18+
const (
19+
partitionSize = 20000000000
20+
)
21+
22+
func DataEasyPartitioning() *schema.Resource {
23+
return &schema.Resource{
24+
ReadContext: dataEasyPartitioningRead,
25+
Schema: map[string]*schema.Schema{
26+
"offer_id": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
Description: "ID of the server offer",
30+
},
31+
"os_id": {
32+
Type: schema.TypeString,
33+
Required: true,
34+
Description: "The base image of the server",
35+
DiffSuppressFunc: dsf.Locality,
36+
ValidateDiagFunc: verify.IsUUIDorUUIDWithLocality(),
37+
},
38+
"swap": {
39+
Type: schema.TypeBool,
40+
Optional: true,
41+
Default: true,
42+
Description: "set swap partition",
43+
},
44+
"extra_partition": {
45+
Type: schema.TypeBool,
46+
Optional: true,
47+
Default: true,
48+
Description: "set extra ext_4 partition",
49+
},
50+
"ext_4_mountpoint": {
51+
Type: schema.TypeString,
52+
Optional: true,
53+
Default: "/data",
54+
Description: "Mount point must be an absolute path with alphanumeric characters and underscores",
55+
},
56+
"json_partition": {
57+
Type: schema.TypeString,
58+
Computed: true,
59+
Description: "The partitioning schema in json format",
60+
},
61+
},
62+
}
63+
}
64+
65+
func dataEasyPartitioningRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
66+
api, fallBackZone, err := newAPIWithZone(d, m)
67+
if err != nil {
68+
return diag.FromErr(err)
69+
}
70+
71+
osID := zonal.ExpandID(d.Get("os_id").(string))
72+
73+
os, err := api.GetOS(&baremetal.GetOSRequest{
74+
Zone: fallBackZone,
75+
OsID: osID.ID,
76+
}, scw.WithContext(ctx))
77+
if err != nil {
78+
return diag.FromErr(err)
79+
}
80+
81+
if !os.CustomPartitioningSupported {
82+
return diag.FromErr(errors.New("custom partitioning is not supported with this OS"))
83+
}
84+
85+
offerID := zonal.ExpandID(d.Get("offer_id").(string))
86+
87+
offer, err := api.GetOffer(&baremetal.GetOfferRequest{
88+
Zone: fallBackZone,
89+
OfferID: offerID.ID,
90+
}, scw.WithContext(ctx))
91+
if err != nil {
92+
return diag.FromErr(err)
93+
}
94+
95+
if !isOSCompatible(offer, os) {
96+
return diag.FromErr(errors.New("OS and offer are not compatible"))
97+
}
98+
99+
defaultPartitioningSchema, err := api.GetDefaultPartitioningSchema(&baremetal.GetDefaultPartitioningSchemaRequest{
100+
Zone: fallBackZone,
101+
OfferID: offerID.ID,
102+
OsID: osID.ID,
103+
}, scw.WithContext(ctx))
104+
if err != nil {
105+
return diag.FromErr(err)
106+
}
107+
108+
extraPart := d.Get("extra_partition").(bool)
109+
swap := d.Get("swap").(bool)
110+
111+
if swap && !extraPart {
112+
jsonSchema, err := json.Marshal(defaultPartitioningSchema)
113+
if err != nil {
114+
return diag.FromErr(err)
115+
}
116+
117+
d.SetId(fmt.Sprintf("%s-%s", offerID, osID))
118+
_ = d.Set("json_partition", string(jsonSchema))
119+
120+
return nil
121+
}
122+
123+
resizeRootPartition(defaultPartitioningSchema.Disks, swap, extraPart)
124+
defaultPartitioningSchema.Disks = handleSwapPartitions(defaultPartitioningSchema.Disks, extraPart, swap)
125+
126+
mountpoint := d.Get("ext_4_mountpoint").(string)
127+
addExtraExt4Partition(mountpoint, defaultPartitioningSchema, extraPart)
128+
129+
if !extraPart && !swap {
130+
defaultPartitioningSchema.Filesystems = defaultPartitioningSchema.Filesystems[:len(defaultPartitioningSchema.Filesystems)-1]
131+
defaultPartitioningSchema.Raids = defaultPartitioningSchema.Raids[:len(defaultPartitioningSchema.Raids)-1]
132+
}
133+
134+
err = api.ValidatePartitioningSchema(&baremetal.ValidatePartitioningSchemaRequest{
135+
Zone: fallBackZone,
136+
OfferID: offerID.ID,
137+
OsID: osID.ID,
138+
PartitioningSchema: defaultPartitioningSchema,
139+
})
140+
if err != nil {
141+
return diag.FromErr(err)
142+
}
143+
144+
jsonSchema, err := json.Marshal(defaultPartitioningSchema)
145+
if err != nil {
146+
return diag.FromErr(err)
147+
}
148+
149+
d.SetId(fmt.Sprintf("%s-%s", offerID, osID))
150+
151+
jsonSchemaStr := string(jsonSchema)
152+
153+
_ = d.Set("json_partition", jsonSchemaStr)
154+
155+
return nil
156+
}
157+
158+
func handleSwapPartitions(originalDisks []*baremetal.SchemaDisk, withExtraPartition bool, swap bool) []*baremetal.SchemaDisk {
159+
if swap {
160+
return originalDisks
161+
}
162+
163+
result := make([]*baremetal.SchemaDisk, 0)
164+
165+
for _, disk := range originalDisks {
166+
i := 1
167+
newPartitions := []*baremetal.SchemaPartition{}
168+
169+
for _, p := range disk.Partitions {
170+
if p.Label == "swap" {
171+
continue
172+
}
173+
174+
if p.Label == "root" {
175+
if !withExtraPartition {
176+
p.Size = 0
177+
p.UseAllAvailableSpace = true
178+
} else {
179+
p.Size = partitionSize
180+
}
181+
}
182+
183+
p.Number = uint32(i)
184+
i++
185+
186+
newPartitions = append(newPartitions, p)
187+
}
188+
189+
result = append(result, &baremetal.SchemaDisk{
190+
Device: disk.Device,
191+
Partitions: newPartitions,
192+
})
193+
}
194+
195+
return result
196+
}
197+
198+
func addExtraExt4Partition(mountpoint string, defaultPartitionSchema *baremetal.Schema, extraPart bool) {
199+
if !extraPart {
200+
return
201+
}
202+
203+
for _, disk := range defaultPartitionSchema.Disks {
204+
partIndex := uint32(len(disk.Partitions)) + 1
205+
data := &baremetal.SchemaPartition{
206+
Label: baremetal.SchemaPartitionLabel("data"),
207+
Number: partIndex,
208+
Size: 0,
209+
UseAllAvailableSpace: true,
210+
}
211+
disk.Partitions = append(disk.Partitions, data)
212+
}
213+
214+
filesystem := &baremetal.SchemaFilesystem{
215+
Device: "/dev/md2",
216+
Format: "ext4",
217+
Mountpoint: mountpoint,
218+
}
219+
defaultPartitionSchema.Filesystems = append(defaultPartitionSchema.Filesystems, filesystem)
220+
}
221+
222+
func resizeRootPartition(originalDisks []*baremetal.SchemaDisk, withSwap bool, withExtraPartition bool) {
223+
for _, disk := range originalDisks {
224+
for _, partition := range disk.Partitions {
225+
if partition.Label == "root" {
226+
if !withSwap && !withExtraPartition {
227+
partition.Size = 0
228+
partition.UseAllAvailableSpace = true
229+
}
230+
231+
if withExtraPartition {
232+
partition.Size = partitionSize
233+
}
234+
}
235+
}
236+
}
237+
}

0 commit comments

Comments
 (0)