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(baremetal): custom partitionning #2820

Merged
merged 11 commits into from
Dec 5, 2024
67 changes: 37 additions & 30 deletions internal/services/baremetal/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package baremetal

import (
"context"
"encoding/json"
"fmt"

"github.com/hashicorp/go-cty/cty"
Expand Down Expand Up @@ -255,7 +256,13 @@ If this behaviour is wanted, please set 'reinstall_on_ssh_key_changes' argument
},
},
},
"partitioning": {
Type: schema.TypeString,
Optional: true,
Description: "The path to the patitioning json file",
},
},

CustomizeDiff: customdiff.Sequence(
cdf.LocalityCheck("private_network.#.id"),
customDiffPrivateNetworkOption(),
Expand Down Expand Up @@ -313,51 +320,51 @@ func ResourceServerCreate(ctx context.Context, d *schema.ResourceData, m interfa
offerID = zonal.NewID(zone, o.ID)
}

if !d.Get("install_config_afterward").(bool) {
if diags := validateInstallConfig(ctx, d, m); len(diags) > 0 {
return diags
}
}

server, err := api.CreateServer(&baremetal.CreateServerRequest{
req := &baremetal.CreateServerRequest{
Zone: zone,
Name: types.ExpandOrGenerateString(d.Get("name"), "bm"),
ProjectID: types.ExpandStringPtr(d.Get("project_id")),
Description: d.Get("description").(string),
OfferID: offerID.ID,
Tags: types.ExpandStrings(d.Get("tags")),
}, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

d.SetId(zonal.NewID(server.Zone, server.ID).String())
partitioningSchema := baremetal.Schema{}
if file, ok := d.GetOk("partitioning"); ok || !d.Get("install_config_afterward").(bool) {
if diags := validateInstallConfig(ctx, d, m); len(diags) > 0 {
return diags
}
if file != "" {
todecode, _ := file.(string)
err = json.Unmarshal([]byte(todecode), &partitioningSchema)
if err != nil {
return diag.FromErr(err)
}
}
req.Install = &baremetal.CreateServerRequestInstall{
OsID: zonal.ExpandID(d.Get("os")).ID,
Hostname: d.Get("hostname").(string),
SSHKeyIDs: types.ExpandStrings(d.Get("ssh_key_ids")),
User: types.ExpandStringPtr(d.Get("user")),
Password: types.ExpandStringPtr(d.Get("password")),
PartitioningSchema: &partitioningSchema,
}
}

_, err = waitForServer(ctx, api, zone, server.ID, d.Timeout(schema.TimeoutCreate))
server, err := api.CreateServer(req, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

if !d.Get("install_config_afterward").(bool) {
_, err = api.InstallServer(&baremetal.InstallServerRequest{
Zone: server.Zone,
ServerID: server.ID,
OsID: zonal.ExpandID(d.Get("os")).ID,
Hostname: types.ExpandStringWithDefault(d.Get("hostname"), server.Name),
SSHKeyIDs: types.ExpandStrings(d.Get("ssh_key_ids")),
User: types.ExpandStringPtr(d.Get("user")),
Password: types.ExpandStringPtr(d.Get("password")),
ServiceUser: types.ExpandStringPtr(d.Get("service_user")),
ServicePassword: types.ExpandStringPtr(d.Get("service_password")),
}, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
d.SetId(zonal.NewID(server.Zone, server.ID).String())

if d.Get("install_config_afterward").(bool) {
_, err = waitForServer(ctx, api, zone, server.ID, d.Timeout(schema.TimeoutCreate))
} else {
_, err = waitForServerInstall(ctx, api, zone, server.ID, d.Timeout(schema.TimeoutCreate))
if err != nil {
return diag.FromErr(err)
}
}
if err != nil {
return diag.FromErr(err)
}

options, optionsExist := d.GetOk("options")
Expand Down
240 changes: 167 additions & 73 deletions internal/services/baremetal/server_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package baremetal_test

import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
Expand All @@ -16,8 +19,9 @@ import (

const SSHKeyBaremetal = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM7HUxRyQtB2rnlhQUcbDGCZcTJg7OvoznOiyC9W6IxH [email protected]"

var jsonConfigPartitioning = "{\"disks\":[{\"device\":\"/dev/nvme0n1\",\"partitions\":[{\"label\":\"uefi\",\"number\":1,\"size\":536870912},{\"label\":\"swap\",\"number\":2,\"size\":4294967296},{\"label\":\"boot\",\"number\":3,\"size\":1073741824},{\"label\":\"root\",\"number\":4,\"size\":1017827045376}]},{\"device\":\"/dev/nvme1n1\",\"partitions\":[{\"label\":\"swap\",\"number\":1,\"size\":4294967296},{\"label\":\"boot\",\"number\":2,\"size\":1073741824},{\"label\":\"root\",\"number\":3,\"size\":1017827045376}]}],\"filesystems\":[{\"device\":\"/dev/nvme0n1p1\",\"format\":\"fat32\",\"mountpoint\":\"/boot/efi\"},{\"device\":\"/dev/md0\",\"format\":\"ext4\",\"mountpoint\":\"/boot\"},{\"device\":\"/dev/md1\",\"format\":\"ext4\",\"mountpoint\":\"/\"}],\"raids\":[{\"devices\":[\"/dev/nvme0n1p3\",\"/dev/nvme1n1p2\"],\"level\":\"raid_level_1\",\"name\":\"/dev/md0\"},{\"devices\":[\"/dev/nvme0n1p4\",\"/dev/nvme1n1p3\"],\"level\":\"raid_level_1\",\"name\":\"/dev/md1\"}],\"zfs\":{\"pools\":[]}}"

func TestAccServer_Basic(t *testing.T) {
// t.Skip("Skipping Baremetal Server test as no stock is available currently")
tt := acctest.NewTestTools(t)
defer tt.Cleanup()
if !IsOfferAvailable(OfferID, Zone, tt) {
Expand Down Expand Up @@ -51,7 +55,7 @@ func TestAccServer_Basic(t *testing.T) {
description = "test a description"
offer = "%s"
os = data.scaleway_baremetal_os.my_os.os_id

tags = [ "terraform-test", "scaleway_baremetal_server", "minimal" ]
ssh_key_ids = [ scaleway_iam_ssh_key.main.id ]
}
Expand Down Expand Up @@ -172,6 +176,63 @@ func TestAccServer_WithoutInstallConfig(t *testing.T) {
})
}

func TestAccServer_CreateServerWithCustomInstallConfig(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()
if !IsOfferAvailable(OfferID, Zone, tt) {
t.Skip("Offer is out of stock")
}

SSHKeyName := "TestAccServer_CreateServerWithCustomInstallConfig"
name := "TestAccServer_CreateServerWithCustomInstallConfig"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: baremetalchecks.CheckServerDestroy(tt),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
data "scaleway_baremetal_os" "my_os" {
zone = "fr-par-1"
name = "Ubuntu"
version = "22.04 LTS (Jammy Jellyfish)"
}

resource "scaleway_iam_ssh_key" "main" {
name = "%s"
public_key = "%s"
}

resource "scaleway_baremetal_server" "base" {
name = "%s"
zone = "fr-par-1"
description = "test a description"
offer = "%s"
os = data.scaleway_baremetal_os.my_os.os_id
partitioning = "%s"

tags = [ "terraform-test", "scaleway_baremetal_server", "minimal" ]
ssh_key_ids = [ scaleway_iam_ssh_key.main.id ]
}
`, SSHKeyName, SSHKeyBaremetal, name, OfferName, strings.ReplaceAll(jsonConfigPartitioning, "\"", "\\\"")),
Check: resource.ComposeTestCheckFunc(
testAccCheckBaremetalServerExists(tt, "scaleway_baremetal_server.base"),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "name", name),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "offer_id", "fr-par-1/206ea234-9097-4ae1-af68-6d2be09f47ed"),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "os", "fr-par-1/96e5f0f2-d216-4de2-8a15-68730d877885"),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "description", "test a description"),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "tags.0", "terraform-test"),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "tags.1", "scaleway_baremetal_server"),
resource.TestCheckResourceAttr("scaleway_baremetal_server.base", "tags.2", "minimal"),
testAccChechPartitioning(tt, "scaleway_baremetal_server.base", jsonConfigPartitioning),
acctest.CheckResourceAttrUUID("scaleway_baremetal_server.base", "ssh_key_ids.0"),
),
},
},
})
}

func TestAccServer_CreateServerWithOption(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()
Expand Down Expand Up @@ -886,82 +947,82 @@ func TestAccServer_WithIPAMPrivateNetwork(t *testing.T) {
},
{
Config: fmt.Sprintf(`
resource "scaleway_vpc" "vpc01" {
name = "TestAccScalewayBaremetalIPAM"
}

resource "scaleway_vpc_private_network" "pn01" {
name = "TestAccScalewayBaremetalIPAM"
ipv4_subnet {
subnet = "172.16.64.0/22"
}
vpc_id = scaleway_vpc.vpc01.id
}

resource "scaleway_ipam_ip" "ip01" {
address = "172.16.64.7"
source {
private_network_id = scaleway_vpc_private_network.pn01.id
}
}

resource "scaleway_ipam_ip" "ip02" {
address = "172.16.64.9"
source {
private_network_id = scaleway_vpc_private_network.pn01.id
}
}

data "scaleway_baremetal_os" "my_os" {
zone = "fr-par-1"
name = "Ubuntu"
version = "22.04 LTS (Jammy Jellyfish)"
}

data "scaleway_baremetal_offer" "my_offer" {
zone = "fr-par-1"
name = "%s"
}

data "scaleway_baremetal_option" "private_network" {
zone = "fr-par-1"
name = "Private Network"
}

resource "scaleway_iam_ssh_key" "base" {
name = "%s"
public_key = "%s"
}

resource "scaleway_baremetal_server" "base" {
name = "%s"
zone = "fr-par-1"
offer = data.scaleway_baremetal_offer.my_offer.offer_id
os = data.scaleway_baremetal_os.my_os.os_id

ssh_key_ids = [ scaleway_iam_ssh_key.base.id ]
options {
id = data.scaleway_baremetal_option.private_network.option_id
resource "scaleway_vpc" "vpc01" {
name = "TestAccScalewayBaremetalIPAM"
}
private_network {
id = scaleway_vpc_private_network.pn01.id
ipam_ip_ids = [scaleway_ipam_ip.ip01.id, scaleway_ipam_ip.ip02.id]

resource "scaleway_vpc_private_network" "pn01" {
name = "TestAccScalewayBaremetalIPAM"
ipv4_subnet {
subnet = "172.16.64.0/22"
}
vpc_id = scaleway_vpc.vpc01.id
}
}

data "scaleway_ipam_ips" "base" {
resource {
name = scaleway_baremetal_server.base.name
type = "baremetal_private_nic"
}
type = "ipv4"
}
`, OfferName, SSHKeyName, SSHKeyBaremetal, name),

resource "scaleway_ipam_ip" "ip01" {
address = "172.16.64.7"
source {
private_network_id = scaleway_vpc_private_network.pn01.id
}
}

resource "scaleway_ipam_ip" "ip02" {
address = "172.16.64.9"
source {
private_network_id = scaleway_vpc_private_network.pn01.id
}
}

data "scaleway_baremetal_os" "my_os" {
zone = "fr-par-1"
name = "Ubuntu"
version = "22.04 LTS (Jammy Jellyfish)"
}

data "scaleway_baremetal_offer" "my_offer" {
zone = "fr-par-1"
name = "%s"
}

data "scaleway_baremetal_option" "private_network" {
zone = "fr-par-1"
name = "Private Network"
}

resource "scaleway_iam_ssh_key" "base" {
name = "%s"
public_key = "%s"
}

resource "scaleway_baremetal_server" "base" {
name = "%s"
zone = "fr-par-1"
offer = data.scaleway_baremetal_offer.my_offer.offer_id
os = data.scaleway_baremetal_os.my_os.os_id

ssh_key_ids = [ scaleway_iam_ssh_key.base.id ]
options {
id = data.scaleway_baremetal_option.private_network.option_id
}
private_network {
id = scaleway_vpc_private_network.pn01.id
ipam_ip_ids = [scaleway_ipam_ip.ip01.id, scaleway_ipam_ip.ip02.id]
}
}

data "scaleway_ipam_ips" "base" {
resource {
name = scaleway_baremetal_server.base.name
type = "baremetal_private_nic"
}
type = "ipv4"
}
`, OfferName, SSHKeyName, SSHKeyBaremetal, name),
Check: resource.ComposeTestCheckFunc(
testAccCheckBaremetalServerExists(tt, "scaleway_baremetal_server.base"),
testAccCheckBaremetalServerHasPrivateNetwork(tt, "scaleway_baremetal_server.base"),
resource.TestCheckResourceAttrPair("scaleway_ipam_ip.ip01", "address", "data.scaleway_ipam_ips.base", "ips.1.address"),
resource.TestCheckResourceAttrPair("scaleway_ipam_ip.ip02", "address", "data.scaleway_ipam_ips.base", "ips.0.address"),
resource.TestCheckResourceAttrPair("scaleway_ipam_ip.ip01", "address", "data.scaleway_ipam_ips.base", "ips.0.address"),
resource.TestCheckResourceAttrPair("scaleway_ipam_ip.ip02", "address", "data.scaleway_ipam_ips.base", "ips.1.address"),
),
},
},
Expand Down Expand Up @@ -992,6 +1053,39 @@ func testAccCheckBaremetalServerExists(tt *acctest.TestTools, n string) resource
}
}

func testAccChechPartitioning(tt *acctest.TestTools, n string, source string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("resource not found: %s", n)
}
baremetalAPI, zonedID, err := baremetal.NewAPIWithZoneAndID(tt.Meta, rs.Primary.ID)
if err != nil {
return err
}

server, err := baremetalAPI.GetServer(&baremetalSDK.GetServerRequest{
ServerID: zonedID.ID,
Zone: zonedID.Zone,
})
if err != nil {
return err
}
if server.Install.PartitioningSchema == nil {
return fmt.Errorf("server %s has no partitioning schema", n)
}
schema := baremetalSDK.Schema{}
err = json.Unmarshal([]byte(source), &schema)
if err != nil {
return err
}
if !reflect.DeepEqual(&schema, server.Install.PartitioningSchema) {
return fmt.Errorf("server %s has not custom partitioning install", n)
}
return nil
}
}

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