diff --git a/docs/data-sources/volume.md b/docs/data-sources/volume.md
index 1b1e4064f..7dfed8d49 100644
--- a/docs/data-sources/volume.md
+++ b/docs/data-sources/volume.md
@@ -35,6 +35,7 @@ data "stackit_volume" "example" {
- `availability_zone` (String) The availability zone of the volume.
- `description` (String) The description of the volume.
+- `encrypted` (Boolean) Indicates if the volume is encrypted.
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`volume_id`".
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container
- `name` (String) The name of the volume.
diff --git a/docs/resources/volume.md b/docs/resources/volume.md
index 0e61bb138..125fed296 100644
--- a/docs/resources/volume.md
+++ b/docs/resources/volume.md
@@ -4,11 +4,14 @@ page_title: "stackit_volume Resource - stackit"
subcategory: ""
description: |-
Volume resource schema. Must have a region specified in the provider configuration.
+ -> Note: Write-Only argument key_payload_base64_wo is available to use in place of key_payload_base64. Write-Only arguments are supported in HashiCorp Terraform 1.11.0 and later. Learn more https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments.
---
# stackit_volume (Resource)
-Volume resource schema. Must have a `region` specified in the provider configuration.
+Volume resource schema. Must have a `region` specified in the provider configuration.
+
+-> **Note:** Write-Only argument `key_payload_base64_wo` is available to use in place of `key_payload_base64`. Write-Only arguments are supported in HashiCorp Terraform 1.11.0 and later. [Learn more](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments).
## Example Usage
@@ -41,6 +44,7 @@ import {
### Optional
- `description` (String) The description of the volume.
+- `encryption_parameters` (Attributes) Parameter to connect to a key-encryption-key within the STACKIT-KMS to create encrypted volumes. These parameters never leave the backend again. So these parameters are not present on imports or in the datasource. They live only in your Terraform state after creation of the resource. (see [below for nested schema](#nestedatt--encryption_parameters))
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container
- `name` (String) The name of the volume.
- `performance_class` (String) The performance class of the volume. Possible values are documented in [Service plans BlockStorage](https://docs.stackit.cloud/products/storage/block-storage/basics/service-plans/#currently-available-service-plans-performance-classes)
@@ -50,10 +54,28 @@ import {
### Read-Only
+- `encrypted` (Boolean) Indicates if the volume is encrypted.
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`volume_id`".
- `server_id` (String) The server ID of the server to which the volume is attached to.
- `volume_id` (String) The volume ID.
+
+### Nested Schema for `encryption_parameters`
+
+Required:
+
+- `kek_key_id` (String) UUID of the key within the STACKIT-KMS to use for the encryption.
+- `kek_key_version` (Number) Version of the key within the STACKIT-KMS to use for the encryption.
+- `kek_keyring_id` (String) UUID of the keyring where the key is located within the STACKTI-KMS.
+- `service_account` (String) Service-Account linked to the Key within the STACKIT-KMS.
+
+Optional:
+
+- `key_payload_base64` (String, Sensitive) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded.
+- `key_payload_base64_wo` (String, Sensitive, [Write-only](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments)) Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded.
+- `key_payload_base64_wo_version` (Number) Used together with `key_payload_base64_wo` to trigger an re-create. Increment this value when an update to `key_payload_base64_wo` is required.
+
+
### Nested Schema for `source`
diff --git a/stackit/internal/services/iaas/iaas_acc_test.go b/stackit/internal/services/iaas/iaas_acc_test.go
index a44aa75a3..58377f887 100644
--- a/stackit/internal/services/iaas/iaas_acc_test.go
+++ b/stackit/internal/services/iaas/iaas_acc_test.go
@@ -225,13 +225,15 @@ var testConfigVolumeVarsMinUpdated = func() config.Variables {
// VOLUME - MAX
var testConfigVolumeVarsMax = config.Variables{
- "project_id": config.StringVariable(testutil.ProjectId),
- "availability_zone": config.StringVariable("eu01-1"),
- "name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
- "size": config.IntegerVariable(16),
- "description": config.StringVariable("description"),
- "performance_class": config.StringVariable("storage_premium_perf0"),
- "label": config.StringVariable("label"),
+ "project_id": config.StringVariable(testutil.ProjectId),
+ "availability_zone": config.StringVariable("eu01-1"),
+ "name": config.StringVariable(fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
+ "size": config.IntegerVariable(16),
+ "description": config.StringVariable("description"),
+ "performance_class": config.StringVariable("storage_premium_perf0"),
+ "label": config.StringVariable("label"),
+ "service_account_mail": config.StringVariable(testutil.TestProjectServiceAccountEmail),
+ "key_payload_base64": config.StringVariable("Y2hhbmdpbmdwbGFuc29mdGJhcmtmaXJzdGNoYW5nZXJlZGh1bmdkb29uY2VoaXN0b3I="),
}
var testConfigVolumeVarsMaxUpdated = func() config.Variables {
@@ -1492,6 +1494,8 @@ func TestAccVolumeMin(t *testing.T) {
resource.TestCheckResourceAttr("stackit_volume.volume_size", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMin["size"])),
resource.TestCheckResourceAttrSet("stackit_volume.volume_size", "performance_class"),
resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_size", "encrypted", "false"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "encryption_parameters"),
// Volume source
resource.TestCheckResourceAttr("stackit_volume.volume_source", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMin["project_id"])),
@@ -1506,6 +1510,8 @@ func TestAccVolumeMin(t *testing.T) {
),
resource.TestCheckResourceAttr("stackit_volume.volume_source", "source.type", "volume"),
resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_source", "encrypted", "false"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "encryption_parameters"),
),
},
// Data source
@@ -1539,6 +1545,7 @@ func TestAccVolumeMin(t *testing.T) {
resource.TestCheckResourceAttrSet("data.stackit_volume.volume_size", "performance_class"),
resource.TestCheckNoResourceAttr("data.stackit_volume.volume_size", "server_id"),
resource.TestCheckResourceAttr("data.stackit_volume.volume_size", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMin["size"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_size", "encrypted", "false"),
// Volume source
resource.TestCheckResourceAttr("data.stackit_volume.volume_source", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMin["project_id"])),
@@ -1555,6 +1562,7 @@ func TestAccVolumeMin(t *testing.T) {
"data.stackit_volume.volume_size", "volume_id",
),
resource.TestCheckResourceAttr("data.stackit_volume.volume_source", "source.type", "volume"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_source", "encrypted", "false"),
),
},
// Import
@@ -1605,6 +1613,8 @@ func TestAccVolumeMin(t *testing.T) {
resource.TestCheckResourceAttr("stackit_volume.volume_size", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMinUpdated["size"])),
resource.TestCheckResourceAttrSet("stackit_volume.volume_size", "performance_class"),
resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "server_id"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "encryption_parameters"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_size", "encrypted", "false"),
// Volume source
resource.TestCheckResourceAttr("stackit_volume.volume_source", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMinUpdated["project_id"])),
@@ -1620,6 +1630,8 @@ func TestAccVolumeMin(t *testing.T) {
),
resource.TestCheckResourceAttr("stackit_volume.volume_source", "source.type", "volume"),
resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "server_id"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "encryption_parameters"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_source", "encrypted", "false"),
),
},
// Deletion is done by the framework implicitly
@@ -1650,6 +1662,8 @@ func TestAccVolumeMax(t *testing.T) {
resource.TestCheckResourceAttr("stackit_volume.volume_size", "labels.%", "1"),
resource.TestCheckResourceAttr("stackit_volume.volume_size", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "server_id"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "encryption_parameters"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_size", "encrypted", "false"),
// Volume source
resource.TestCheckResourceAttr("stackit_volume.volume_source", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
@@ -1668,6 +1682,89 @@ func TestAccVolumeMax(t *testing.T) {
),
resource.TestCheckResourceAttr("stackit_volume.volume_source", "source.type", "volume"),
resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "server_id"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "encryption_parameters"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_source", "encrypted", "false"),
+
+ // Volume encrypted - no key payload
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_no_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["availability_zone"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["description"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["size"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_no_key_payload", "performance_class"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encrypted", "true"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "key_id",
+ "stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.kek_key_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.kek_key_version", "1"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "keyring_id",
+ "stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.kek_keyring_id",
+ ),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.key_payload_base64"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.key_payload_base64_wo"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.key_payload_base64_wo_version"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.service_account", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["service_account_mail"])),
+
+ // Volume encrypted - with regular key payload
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_regular_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["availability_zone"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["description"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["size"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_regular_key_payload", "performance_class"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encrypted", "true"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "key_id",
+ "stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.kek_key_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.kek_key_version", "1"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "keyring_id",
+ "stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.kek_keyring_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.key_payload_base64", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["key_payload_base64"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.key_payload_base64_wo"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.key_payload_base64_wo_version"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.service_account", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["service_account_mail"])),
+
+ // Volume encrypted - with write-only key payload
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_write_only_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["availability_zone"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["description"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["size"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_write_only_key_payload", "performance_class"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encrypted", "true"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "key_id",
+ "stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.kek_key_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.kek_key_version", "1"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "keyring_id",
+ "stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.kek_keyring_id",
+ ),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.key_payload_base64"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.key_payload_base64_wo"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.key_payload_base64_wo_version", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.service_account", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["service_account_mail"])),
),
},
// Data source
@@ -1686,6 +1783,21 @@ func TestAccVolumeMax(t *testing.T) {
project_id = stackit_volume.volume_source.project_id
volume_id = stackit_volume.volume_source.volume_id
}
+
+ data "stackit_volume" "volume_encrypted_no_key_payload" {
+ project_id = stackit_volume.volume_encrypted_no_key_payload.project_id
+ volume_id = stackit_volume.volume_encrypted_no_key_payload.volume_id
+ }
+
+ data "stackit_volume" "volume_encrypted_with_regular_key_payload" {
+ project_id = stackit_volume.volume_encrypted_with_regular_key_payload.project_id
+ volume_id = stackit_volume.volume_encrypted_with_regular_key_payload.volume_id
+ }
+
+ data "stackit_volume" "volume_encrypted_with_write_only_key_payload" {
+ project_id = stackit_volume.volume_encrypted_with_write_only_key_payload.project_id
+ volume_id = stackit_volume.volume_encrypted_with_write_only_key_payload.volume_id
+ }
`,
testutil.IaaSProviderConfig(), resourceVolumeMaxConfig,
),
@@ -1705,6 +1817,7 @@ func TestAccVolumeMax(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_volume.volume_size", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
resource.TestCheckResourceAttr("data.stackit_volume.volume_size", "labels.%", "1"),
resource.TestCheckResourceAttr("data.stackit_volume.volume_size", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_size", "encrypted", "false"),
// Volume source
resource.TestCheckResourceAttr("data.stackit_volume.volume_source", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
@@ -1726,6 +1839,49 @@ func TestAccVolumeMax(t *testing.T) {
),
resource.TestCheckResourceAttr("data.stackit_volume.volume_source", "source.type", "volume"),
resource.TestCheckNoResourceAttr("data.stackit_volume.volume_source", "server_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_source", "encrypted", "false"),
+
+ // Volume encrypted - no key payload
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
+ resource.TestCheckResourceAttrSet("data.stackit_volume.volume_encrypted_no_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["availability_zone"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["size"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["description"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "performance_class", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["performance_class"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckNoResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_no_key_payload", "encrypted", "true"),
+
+ // Volume encrypted - with regular key payload
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
+ resource.TestCheckResourceAttrSet("data.stackit_volume.volume_encrypted_with_regular_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["availability_zone"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["size"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["description"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "performance_class", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["performance_class"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckNoResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_regular_key_payload", "encrypted", "true"),
+
+ // Volume encrypted - with write-only key payload
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["project_id"])),
+ resource.TestCheckResourceAttrSet("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["availability_zone"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["size"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["description"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "performance_class", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["performance_class"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["name"])),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMax["label"])),
+ resource.TestCheckNoResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("data.stackit_volume.volume_encrypted_with_write_only_key_payload", "encrypted", "true"),
),
},
// Import
@@ -1763,6 +1919,84 @@ func TestAccVolumeMax(t *testing.T) {
ImportState: true,
ImportStateVerify: true,
},
+ {
+ ConfigVariables: testConfigVolumeVarsMax,
+ ResourceName: "stackit_volume.volume_encrypted_no_key_payload",
+ ImportStateIdFunc: func(s *terraform.State) (string, error) {
+ r, ok := s.RootModule().Resources["stackit_volume.volume_encrypted_no_key_payload"]
+ if !ok {
+ return "", fmt.Errorf("couldn't find resource stackit_volume.volume_encrypted_no_key_payload")
+ }
+ volumeId, ok := r.Primary.Attributes["volume_id"]
+ if !ok {
+ return "", fmt.Errorf("couldn't find attribute volume_id")
+ }
+ return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, volumeId), nil
+ },
+ ImportState: true,
+ ImportStateVerify: true,
+ // the values below won't be imported, as they can be only **sent** to the API, but will be **never returned**
+ ImportStateVerifyIgnore: []string{
+ "encryption_parameters",
+ "encryption_parameters.kek_key_id",
+ "encryption_parameters.kek_key_version",
+ "encryption_parameters.kek_keyring_id",
+ "encryption_parameters.key_payload_base64",
+ "encryption_parameters.service_account",
+ },
+ },
+ {
+ ConfigVariables: testConfigVolumeVarsMax,
+ ResourceName: "stackit_volume.volume_encrypted_with_regular_key_payload",
+ ImportStateIdFunc: func(s *terraform.State) (string, error) {
+ r, ok := s.RootModule().Resources["stackit_volume.volume_encrypted_with_regular_key_payload"]
+ if !ok {
+ return "", fmt.Errorf("couldn't find resource stackit_volume.volume_encrypted_with_regular_key_payload")
+ }
+ volumeId, ok := r.Primary.Attributes["volume_id"]
+ if !ok {
+ return "", fmt.Errorf("couldn't find attribute volume_id")
+ }
+ return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, volumeId), nil
+ },
+ ImportState: true,
+ ImportStateVerify: true,
+ // the values below won't be imported, as they can be only **sent** to the API, but will be **never returned**
+ ImportStateVerifyIgnore: []string{
+ "encryption_parameters",
+ "encryption_parameters.kek_key_id",
+ "encryption_parameters.kek_key_version",
+ "encryption_parameters.kek_keyring_id",
+ "encryption_parameters.key_payload_base64",
+ "encryption_parameters.service_account",
+ },
+ },
+ {
+ ConfigVariables: testConfigVolumeVarsMax,
+ ResourceName: "stackit_volume.volume_encrypted_with_write_only_key_payload",
+ ImportStateIdFunc: func(s *terraform.State) (string, error) {
+ r, ok := s.RootModule().Resources["stackit_volume.volume_encrypted_with_write_only_key_payload"]
+ if !ok {
+ return "", fmt.Errorf("couldn't find resource stackit_volume.volume_encrypted_with_write_only_key_payload")
+ }
+ volumeId, ok := r.Primary.Attributes["volume_id"]
+ if !ok {
+ return "", fmt.Errorf("couldn't find attribute volume_id")
+ }
+ return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, volumeId), nil
+ },
+ ImportState: true,
+ ImportStateVerify: true,
+ // the values below won't be imported, as they can be only **sent** to the API, but will be **never returned**
+ ImportStateVerifyIgnore: []string{
+ "encryption_parameters",
+ "encryption_parameters.kek_key_id",
+ "encryption_parameters.kek_key_version",
+ "encryption_parameters.kek_keyring_id",
+ "encryption_parameters.key_payload_base64",
+ "encryption_parameters.service_account",
+ },
+ },
// Update
{
ConfigVariables: testConfigVolumeVarsMaxUpdated,
@@ -1779,6 +2013,8 @@ func TestAccVolumeMax(t *testing.T) {
resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "server_id"),
resource.TestCheckResourceAttr("stackit_volume.volume_size", "labels.%", "1"),
resource.TestCheckResourceAttr("stackit_volume.volume_size", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_size", "encryption_parameters"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_size", "encrypted", "false"),
// Volume source
resource.TestCheckResourceAttr("stackit_volume.volume_source", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["project_id"])),
@@ -1796,6 +2032,89 @@ func TestAccVolumeMax(t *testing.T) {
),
resource.TestCheckResourceAttr("stackit_volume.volume_source", "source.type", "volume"),
resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "server_id"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_source", "encryption_parameters"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_source", "encrypted", "false"),
+
+ // Volume encrypted - no key payload
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["project_id"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_no_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["availability_zone"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["description"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["size"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_no_key_payload", "performance_class"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["name"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encrypted", "true"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "key_id",
+ "stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.kek_key_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.kek_key_version", "1"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "keyring_id",
+ "stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.kek_keyring_id",
+ ),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.key_payload_base64"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.key_payload_base64_wo"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.key_payload_base64_wo_version"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_no_key_payload", "encryption_parameters.service_account", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["service_account_mail"])),
+
+ // Volume encrypted - with regular key payload
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["project_id"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_regular_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["availability_zone"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["description"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["size"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_regular_key_payload", "performance_class"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["name"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encrypted", "true"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "key_id",
+ "stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.kek_key_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.kek_key_version", "1"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "keyring_id",
+ "stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.kek_keyring_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.key_payload_base64", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["key_payload_base64"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.key_payload_base64_wo"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.key_payload_base64_wo_version"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_regular_key_payload", "encryption_parameters.service_account", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["service_account_mail"])),
+
+ // Volume encrypted - with write-only key payload
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "project_id", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["project_id"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_write_only_key_payload", "volume_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "region", testutil.Region),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "availability_zone", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["availability_zone"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "description", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["description"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "size", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["size"])),
+ resource.TestCheckResourceAttrSet("stackit_volume.volume_encrypted_with_write_only_key_payload", "performance_class"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "name", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["name"])),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "labels.%", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "labels.acc-test", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["label"])),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "server_id"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encrypted", "true"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "key_id",
+ "stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.kek_key_id",
+ ),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.kek_key_version", "1"),
+ resource.TestCheckResourceAttrPair(
+ "stackit_kms_key.key", "keyring_id",
+ "stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.kek_keyring_id",
+ ),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.key_payload_base64"),
+ resource.TestCheckNoResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.key_payload_base64_wo"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.key_payload_base64_wo_version", "1"),
+ resource.TestCheckResourceAttr("stackit_volume.volume_encrypted_with_write_only_key_payload", "encryption_parameters.service_account", testutil.ConvertConfigVariable(testConfigVolumeVarsMaxUpdated["service_account_mail"])),
),
},
// Deletion is done by the framework implicitly
diff --git a/stackit/internal/services/iaas/testdata/resource-volume-max.tf b/stackit/internal/services/iaas/testdata/resource-volume-max.tf
index 54c590f63..86641a6c4 100644
--- a/stackit/internal/services/iaas/testdata/resource-volume-max.tf
+++ b/stackit/internal/services/iaas/testdata/resource-volume-max.tf
@@ -5,6 +5,8 @@ variable "size" {}
variable "description" {}
variable "performance_class" {}
variable "label" {}
+variable "key_payload_base64" {}
+variable "service_account_mail" {}
resource "stackit_volume" "volume_size" {
project_id = var.project_id
@@ -33,4 +35,82 @@ resource "stackit_volume" "volume_source" {
labels = {
"acc-test" : var.label
}
-}
\ No newline at end of file
+}
+
+# just needed for the test setup for encrypted volumes
+resource "stackit_kms_keyring" "keyring" {
+ project_id = var.project_id
+ display_name = var.name
+}
+
+# just needed for the test setup for encrypted volumes
+resource "stackit_kms_key" "key" {
+ project_id = var.project_id
+ keyring_id = stackit_kms_keyring.keyring.keyring_id
+ display_name = var.name
+ protection = "software"
+ algorithm = "aes_256_gcm"
+ purpose = "symmetric_encrypt_decrypt"
+}
+
+resource "stackit_volume" "volume_encrypted_no_key_payload" {
+ project_id = var.project_id
+ availability_zone = var.availability_zone
+ name = var.name
+ size = var.size
+ description = var.description
+ performance_class = var.performance_class
+ labels = {
+ "acc-test" : var.label
+ }
+
+ encryption_parameters = {
+ kek_key_id = stackit_kms_key.key.key_id
+ kek_key_version = 1
+ kek_keyring_id = stackit_kms_keyring.keyring.keyring_id
+ service_account = var.service_account_mail
+ }
+}
+
+# use the regular "key_payload_base64" field
+resource "stackit_volume" "volume_encrypted_with_regular_key_payload" {
+ project_id = var.project_id
+ availability_zone = var.availability_zone
+ name = var.name
+ size = var.size
+ description = var.description
+ performance_class = var.performance_class
+ labels = {
+ "acc-test" : var.label
+ }
+
+ encryption_parameters = {
+ kek_key_id = stackit_kms_key.key.key_id
+ kek_key_version = 1
+ kek_keyring_id = stackit_kms_keyring.keyring.keyring_id
+ key_payload_base64 = var.key_payload_base64
+ service_account = var.service_account_mail
+ }
+}
+
+# use the write-only "key_payload_base64_wo" field instead
+resource "stackit_volume" "volume_encrypted_with_write_only_key_payload" {
+ project_id = var.project_id
+ availability_zone = var.availability_zone
+ name = var.name
+ size = var.size
+ description = var.description
+ performance_class = var.performance_class
+ labels = {
+ "acc-test" : var.label
+ }
+
+ encryption_parameters = {
+ kek_key_id = stackit_kms_key.key.key_id
+ kek_key_version = 1
+ kek_keyring_id = stackit_kms_keyring.keyring.keyring_id
+ key_payload_base64_wo = var.key_payload_base64
+ key_payload_base64_wo_version = 1
+ service_account = var.service_account_mail
+ }
+}
diff --git a/stackit/internal/services/iaas/volume/datasource.go b/stackit/internal/services/iaas/volume/datasource.go
index 5e36a3950..d3553037c 100644
--- a/stackit/internal/services/iaas/volume/datasource.go
+++ b/stackit/internal/services/iaas/volume/datasource.go
@@ -5,6 +5,10 @@ import (
"fmt"
"net/http"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
@@ -24,6 +28,23 @@ var (
_ datasource.DataSource = &volumeDataSource{}
)
+type DatasourceModel struct {
+ // basically the same as the resource model, just without encryption parameters as they are only **sent** to the API, but **never returned**
+ Id types.String `tfsdk:"id"` // needed by TF
+ ProjectId types.String `tfsdk:"project_id"`
+ Region types.String `tfsdk:"region"`
+ VolumeId types.String `tfsdk:"volume_id"`
+ Name types.String `tfsdk:"name"`
+ AvailabilityZone types.String `tfsdk:"availability_zone"`
+ Labels types.Map `tfsdk:"labels"`
+ Description types.String `tfsdk:"description"`
+ PerformanceClass types.String `tfsdk:"performance_class"`
+ Size types.Int64 `tfsdk:"size"`
+ ServerId types.String `tfsdk:"server_id"`
+ Source types.Object `tfsdk:"source"`
+ Encrypted types.Bool `tfsdk:"encrypted"`
+}
+
// NewVolumeDataSource is a helper function to simplify the provider implementation.
func NewVolumeDataSource() datasource.DataSource {
return &volumeDataSource{}
@@ -134,13 +155,17 @@ func (d *volumeDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
},
},
},
+ "encrypted": schema.BoolAttribute{
+ Description: "Indicates if the volume is encrypted.",
+ Computed: true,
+ },
},
}
}
// Read refreshes the Terraform state with the latest data.
func (d *volumeDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
- var model Model
+ var model DatasourceModel
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
@@ -174,7 +199,7 @@ func (d *volumeDataSource) Read(ctx context.Context, req datasource.ReadRequest,
ctx = core.LogResponse(ctx)
- err = mapFields(ctx, volumeResp, &model, region)
+ err = mapDatasourceFields(ctx, volumeResp, &model, region)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading volume", fmt.Sprintf("Processing API payload: %v", err))
return
@@ -186,3 +211,62 @@ func (d *volumeDataSource) Read(ctx context.Context, req datasource.ReadRequest,
}
tflog.Info(ctx, "volume read")
}
+
+func mapDatasourceFields(ctx context.Context, volumeResp *iaas.Volume, model *DatasourceModel, region string) error {
+ if volumeResp == nil {
+ return fmt.Errorf("response input is nil")
+ }
+ if model == nil {
+ return fmt.Errorf("model input is nil")
+ }
+
+ var volumeId string
+ if model.VolumeId.ValueString() != "" {
+ volumeId = model.VolumeId.ValueString()
+ } else if volumeResp.Id != nil {
+ volumeId = *volumeResp.Id
+ } else {
+ return fmt.Errorf("Volume id not present")
+ }
+
+ model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, volumeId)
+ model.Region = types.StringValue(region)
+
+ labels, err := iaasUtils.MapLabels(ctx, volumeResp.Labels, model.Labels)
+ if err != nil {
+ return err
+ }
+
+ var sourceValues map[string]attr.Value
+ var sourceObject basetypes.ObjectValue
+ if volumeResp.Source == nil {
+ sourceObject = types.ObjectNull(sourceTypes)
+ } else {
+ sourceValues = map[string]attr.Value{
+ "type": types.StringPointerValue(volumeResp.Source.Type),
+ "id": types.StringPointerValue(volumeResp.Source.Id),
+ }
+ var diags diag.Diagnostics
+ sourceObject, diags = types.ObjectValue(sourceTypes, sourceValues)
+ if diags.HasError() {
+ return fmt.Errorf("creating source: %w", core.DiagsToError(diags))
+ }
+ }
+
+ model.VolumeId = types.StringValue(volumeId)
+ model.AvailabilityZone = types.StringPointerValue(volumeResp.AvailabilityZone)
+ model.Description = types.StringPointerValue(volumeResp.Description)
+ model.Name = types.StringPointerValue(volumeResp.Name)
+ // Workaround for volumes with no names which return an empty string instead of nil
+ if name := volumeResp.Name; name != nil && *name == "" {
+ model.Name = types.StringNull()
+ }
+ model.Labels = labels
+ model.PerformanceClass = types.StringPointerValue(volumeResp.PerformanceClass)
+ model.ServerId = types.StringPointerValue(volumeResp.ServerId)
+ model.Size = types.Int64PointerValue(volumeResp.Size)
+ model.Source = sourceObject
+ model.Encrypted = types.BoolPointerValue(volumeResp.Encrypted)
+
+ return nil
+}
diff --git a/stackit/internal/services/iaas/volume/datasource_test.go b/stackit/internal/services/iaas/volume/datasource_test.go
new file mode 100644
index 000000000..e5b505576
--- /dev/null
+++ b/stackit/internal/services/iaas/volume/datasource_test.go
@@ -0,0 +1,172 @@
+package volume
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/stackitcloud/stackit-sdk-go/core/utils"
+ "github.com/stackitcloud/stackit-sdk-go/services/iaas"
+)
+
+func TestMapDatasourceFields(t *testing.T) {
+ type args struct {
+ state DatasourceModel
+ input *iaas.Volume
+ region string
+ }
+ tests := []struct {
+ description string
+ args args
+ expected DatasourceModel
+ isValid bool
+ }{
+ {
+ description: "default_values",
+ args: args{
+ state: DatasourceModel{
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ },
+ input: &iaas.Volume{
+ Id: utils.Ptr("nid"),
+ EncryptionParameters: nil,
+ },
+ region: "eu01",
+ },
+ expected: DatasourceModel{
+ Id: types.StringValue("pid,eu01,nid"),
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ Name: types.StringNull(),
+ AvailabilityZone: types.StringNull(),
+ Labels: types.MapNull(types.StringType),
+ Description: types.StringNull(),
+ PerformanceClass: types.StringNull(),
+ ServerId: types.StringNull(),
+ Size: types.Int64Null(),
+ Source: types.ObjectNull(sourceTypes),
+ Region: types.StringValue("eu01"),
+ },
+ isValid: true,
+ },
+ {
+ description: "simple_values",
+ args: args{
+ state: DatasourceModel{
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ Region: types.StringValue("eu01"),
+ },
+ input: &iaas.Volume{
+ Id: utils.Ptr("nid"),
+ Name: utils.Ptr("name"),
+ AvailabilityZone: utils.Ptr("zone"),
+ Labels: &map[string]interface{}{
+ "key": "value",
+ },
+ Description: utils.Ptr("desc"),
+ PerformanceClass: utils.Ptr("class"),
+ ServerId: utils.Ptr("sid"),
+ Size: utils.Ptr(int64(1)),
+ Source: &iaas.VolumeSource{},
+ Encrypted: utils.Ptr(true),
+ EncryptionParameters: &iaas.VolumeEncryptionParameter{
+ KekKeyId: utils.Ptr("kek-key-id"),
+ KekKeyVersion: utils.Ptr(int64(1)),
+ KekKeyringId: utils.Ptr("kek-keyring-id"),
+ KekProjectId: utils.Ptr("kek-project-id"),
+ KeyPayload: nil,
+ ServiceAccount: utils.Ptr("test-sa@sa.stackit.cloud"),
+ },
+ },
+ region: "eu02",
+ },
+ expected: DatasourceModel{
+ Id: types.StringValue("pid,eu02,nid"),
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ Name: types.StringValue("name"),
+ AvailabilityZone: types.StringValue("zone"),
+ Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
+ "key": types.StringValue("value"),
+ }),
+ Description: types.StringValue("desc"),
+ PerformanceClass: types.StringValue("class"),
+ ServerId: types.StringValue("sid"),
+ Size: types.Int64Value(1),
+ Source: types.ObjectValueMust(sourceTypes, map[string]attr.Value{
+ "type": types.StringNull(),
+ "id": types.StringNull(),
+ }),
+ Region: types.StringValue("eu02"),
+ Encrypted: types.BoolValue(true),
+ },
+ isValid: true,
+ },
+ {
+ description: "empty labels and encryption parameters",
+ args: args{
+ state: DatasourceModel{
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}),
+ },
+ input: &iaas.Volume{
+ Id: utils.Ptr("nid"),
+ EncryptionParameters: &iaas.VolumeEncryptionParameter{},
+ },
+ region: "eu01",
+ },
+ expected: DatasourceModel{
+ Id: types.StringValue("pid,eu01,nid"),
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ Name: types.StringNull(),
+ AvailabilityZone: types.StringNull(),
+ Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}),
+ Description: types.StringNull(),
+ PerformanceClass: types.StringNull(),
+ ServerId: types.StringNull(),
+ Size: types.Int64Null(),
+ Source: types.ObjectNull(sourceTypes),
+ Region: types.StringValue("eu01"),
+ },
+ isValid: true,
+ },
+ {
+ description: "response_nil_fail",
+ },
+ {
+ description: "no_resource_id",
+ args: args{
+ state: DatasourceModel{
+ ProjectId: types.StringValue("pid"),
+ },
+ input: &iaas.Volume{},
+ },
+ expected: DatasourceModel{},
+ isValid: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ err := mapDatasourceFields(context.Background(), tt.args.input, &tt.args.state, tt.args.region)
+ if !tt.isValid && err == nil {
+ t.Fatalf("Should have failed")
+ }
+ if tt.isValid && err != nil {
+ t.Fatalf("Should not have failed: %v", err)
+ }
+ if tt.isValid {
+ diff := cmp.Diff(tt.args.state, tt.expected)
+ if diff != "" {
+ t.Fatalf("Data does not match: %s", diff)
+ }
+ }
+ })
+ }
+}
diff --git a/stackit/internal/services/iaas/volume/resource.go b/stackit/internal/services/iaas/volume/resource.go
index 1ce0d5d73..39b071fd0 100644
--- a/stackit/internal/services/iaas/volume/resource.go
+++ b/stackit/internal/services/iaas/volume/resource.go
@@ -7,6 +7,11 @@ import (
"regexp"
"strings"
+ "github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
+ sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
+
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
+
iaasUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
@@ -43,18 +48,30 @@ var (
)
type Model struct {
- Id types.String `tfsdk:"id"` // needed by TF
- ProjectId types.String `tfsdk:"project_id"`
- Region types.String `tfsdk:"region"`
- VolumeId types.String `tfsdk:"volume_id"`
- Name types.String `tfsdk:"name"`
- AvailabilityZone types.String `tfsdk:"availability_zone"`
- Labels types.Map `tfsdk:"labels"`
- Description types.String `tfsdk:"description"`
- PerformanceClass types.String `tfsdk:"performance_class"`
- Size types.Int64 `tfsdk:"size"`
- ServerId types.String `tfsdk:"server_id"`
- Source types.Object `tfsdk:"source"`
+ Id types.String `tfsdk:"id"` // needed by TF
+ ProjectId types.String `tfsdk:"project_id"`
+ Region types.String `tfsdk:"region"`
+ VolumeId types.String `tfsdk:"volume_id"`
+ Name types.String `tfsdk:"name"`
+ AvailabilityZone types.String `tfsdk:"availability_zone"`
+ Labels types.Map `tfsdk:"labels"`
+ Description types.String `tfsdk:"description"`
+ PerformanceClass types.String `tfsdk:"performance_class"`
+ Size types.Int64 `tfsdk:"size"`
+ ServerId types.String `tfsdk:"server_id"`
+ Source types.Object `tfsdk:"source"`
+ EncryptionParameters *encryptionParametersModel `tfsdk:"encryption_parameters"`
+ Encrypted types.Bool `tfsdk:"encrypted"`
+}
+
+type encryptionParametersModel struct {
+ KekKeyId types.String `tfsdk:"kek_key_id"`
+ KekKeyVersion types.Int64 `tfsdk:"kek_key_version"`
+ KekKeyringId types.String `tfsdk:"kek_keyring_id"`
+ KeyPayloadBase64 types.String `tfsdk:"key_payload_base64"`
+ KeyPayloadBase64WriteOnly types.String `tfsdk:"key_payload_base64_wo"`
+ KeyPayloadBase64WriteOnlyVersion types.Int64 `tfsdk:"key_payload_base64_wo_version"`
+ ServiceAccount types.String `tfsdk:"service_account"`
}
// Struct corresponding to Model.Source
@@ -145,7 +162,7 @@ func (r *volumeResource) Configure(ctx context.Context, req resource.ConfigureRe
func (r *volumeResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
description := "Volume resource schema. Must have a `region` specified in the provider configuration."
resp.Schema = schema.Schema{
- MarkdownDescription: description,
+ MarkdownDescription: fmt.Sprintf("%s \n\n-> **Note:** Write-Only argument `key_payload_base64_wo` is available to use in place of `key_payload_base64`. Write-Only arguments are supported in HashiCorp Terraform 1.11.0 and later. [Learn more](https://developer.hashicorp.com/terraform/language/resources/ephemeral#write-only-arguments).", description),
Description: description,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
@@ -283,6 +300,91 @@ func (r *volumeResource) Schema(_ context.Context, _ resource.SchemaRequest, res
},
},
},
+ "encryption_parameters": schema.SingleNestedAttribute{
+ Description: "Parameter to connect to a key-encryption-key within the STACKIT-KMS to create encrypted volumes. These parameters never leave the backend again. So these parameters are not present on imports or in the datasource. They live only in your Terraform state after creation of the resource.",
+ Optional: true,
+ PlanModifiers: []planmodifier.Object{
+ objectplanmodifier.RequiresReplace(),
+ },
+ Attributes: map[string]schema.Attribute{
+ "kek_key_id": schema.StringAttribute{
+ Description: "UUID of the key within the STACKIT-KMS to use for the encryption.",
+ Required: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.RequiresReplace(),
+ },
+ Validators: []validator.String{
+ validate.UUID(),
+ validate.NoSeparator(),
+ },
+ },
+ "kek_key_version": schema.Int64Attribute{
+ Description: "Version of the key within the STACKIT-KMS to use for the encryption.",
+ Required: true,
+ PlanModifiers: []planmodifier.Int64{
+ int64planmodifier.RequiresReplace(),
+ },
+ },
+ "kek_keyring_id": schema.StringAttribute{
+ Description: "UUID of the keyring where the key is located within the STACKTI-KMS.",
+ Required: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.RequiresReplace(),
+ },
+ Validators: []validator.String{
+ validate.UUID(),
+ validate.NoSeparator(),
+ },
+ },
+ "key_payload_base64": schema.StringAttribute{
+ Description: "Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded.",
+ Optional: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.RequiresReplace(),
+ },
+ Validators: []validator.String{
+ stringvalidator.ConflictsWith(
+ path.MatchRelative().AtParent().AtName("key_payload_base64_wo"),
+ path.MatchRelative().AtParent().AtName("key_payload_base64_wo_version"),
+ ),
+ stringvalidator.PreferWriteOnlyAttribute(path.MatchRoot("encryption_parameters").AtName("key_payload_base64_wo")),
+ },
+ Sensitive: true,
+ },
+ "key_payload_base64_wo": schema.StringAttribute{
+ Description: "Optional predefined secret, which will be encrypted against the key-encryption-key within the STACKIT-KMS. If not defined, a random secret will be generated by the API and encrypted against the STACKIT-KMS. If a key-payload is provided here, it must be base64 encoded.",
+ WriteOnly: true,
+ Optional: true,
+ Sensitive: true,
+ Validators: []validator.String{
+ stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("key_payload_base64")),
+ stringvalidator.AlsoRequires(path.MatchRelative().AtParent().AtName("key_payload_base64_wo_version")),
+ },
+ },
+ "key_payload_base64_wo_version": schema.Int64Attribute{
+ Description: "Used together with `key_payload_base64_wo` to trigger an re-create. Increment this value when an update to `key_payload_base64_wo` is required.",
+ Optional: true,
+ PlanModifiers: []planmodifier.Int64{
+ int64planmodifier.RequiresReplace(),
+ },
+ Validators: []validator.Int64{
+ int64validator.AlsoRequires(path.MatchRelative().AtParent().AtName("key_payload_base64_wo")),
+ int64validator.ConflictsWith(path.MatchRelative().AtParent().AtName("key_payload_base64")),
+ },
+ },
+ "service_account": schema.StringAttribute{
+ Description: "Service-Account linked to the Key within the STACKIT-KMS.",
+ Required: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.RequiresReplace(),
+ },
+ },
+ },
+ },
+ "encrypted": schema.BoolAttribute{
+ Description: "Indicates if the volume is encrypted.",
+ Computed: true,
+ },
},
}
}
@@ -621,6 +723,10 @@ func mapFields(ctx context.Context, volumeResp *iaas.Volume, model *Model, regio
model.ServerId = types.StringPointerValue(volumeResp.ServerId)
model.Size = types.Int64PointerValue(volumeResp.Size)
model.Source = sourceObject
+ model.Encrypted = types.BoolPointerValue(volumeResp.Encrypted)
+
+ // no need to map encryption parameters as they are only **sent** to the API but **never returned**
+
return nil
}
@@ -643,7 +749,7 @@ func toCreatePayload(ctx context.Context, model *Model, source *sourceModel) (*i
}
}
- return &iaas.CreateVolumePayload{
+ payload := iaas.CreateVolumePayload{
AvailabilityZone: conversion.StringValueToPointer(model.AvailabilityZone),
Description: conversion.StringValueToPointer(model.Description),
Labels: &labels,
@@ -651,7 +757,27 @@ func toCreatePayload(ctx context.Context, model *Model, source *sourceModel) (*i
PerformanceClass: conversion.StringValueToPointer(model.PerformanceClass),
Size: conversion.Int64ValueToPointer(model.Size),
Source: sourcePayload,
- }, nil
+ }
+
+ if model.EncryptionParameters != nil {
+ var keyPayload *[]byte
+ if !utils.IsUndefined(model.EncryptionParameters.KeyPayloadBase64WriteOnly) {
+ keyPayload = sdkUtils.Ptr([]byte(model.EncryptionParameters.KeyPayloadBase64WriteOnly.ValueString()))
+ } else if !utils.IsUndefined(model.EncryptionParameters.KeyPayloadBase64) {
+ keyPayload = sdkUtils.Ptr([]byte(model.EncryptionParameters.KeyPayloadBase64.ValueString()))
+ }
+
+ payload.EncryptionParameters = &iaas.VolumeEncryptionParameter{
+ KekKeyId: conversion.StringValueToPointer(model.EncryptionParameters.KekKeyId),
+ KekKeyVersion: conversion.Int64ValueToPointer(model.EncryptionParameters.KekKeyVersion),
+ KekKeyringId: conversion.StringValueToPointer(model.EncryptionParameters.KekKeyringId),
+ KekProjectId: nil,
+ KeyPayload: keyPayload,
+ ServiceAccount: conversion.StringValueToPointer(model.EncryptionParameters.ServiceAccount),
+ }
+ }
+
+ return &payload, nil
}
func toUpdatePayload(ctx context.Context, model *Model, currentLabels types.Map) (*iaas.UpdateVolumePayload, error) {
diff --git a/stackit/internal/services/iaas/volume/resource_test.go b/stackit/internal/services/iaas/volume/resource_test.go
index 14f456a73..b97b1dc6e 100644
--- a/stackit/internal/services/iaas/volume/resource_test.go
+++ b/stackit/internal/services/iaas/volume/resource_test.go
@@ -31,23 +31,25 @@ func TestMapFields(t *testing.T) {
VolumeId: types.StringValue("nid"),
},
input: &iaas.Volume{
- Id: utils.Ptr("nid"),
+ Id: utils.Ptr("nid"),
+ EncryptionParameters: nil,
},
region: "eu01",
},
expected: Model{
- Id: types.StringValue("pid,eu01,nid"),
- ProjectId: types.StringValue("pid"),
- VolumeId: types.StringValue("nid"),
- Name: types.StringNull(),
- AvailabilityZone: types.StringNull(),
- Labels: types.MapNull(types.StringType),
- Description: types.StringNull(),
- PerformanceClass: types.StringNull(),
- ServerId: types.StringNull(),
- Size: types.Int64Null(),
- Source: types.ObjectNull(sourceTypes),
- Region: types.StringValue("eu01"),
+ Id: types.StringValue("pid,eu01,nid"),
+ ProjectId: types.StringValue("pid"),
+ VolumeId: types.StringValue("nid"),
+ Name: types.StringNull(),
+ AvailabilityZone: types.StringNull(),
+ Labels: types.MapNull(types.StringType),
+ Description: types.StringNull(),
+ PerformanceClass: types.StringNull(),
+ ServerId: types.StringNull(),
+ Size: types.Int64Null(),
+ Source: types.ObjectNull(sourceTypes),
+ Region: types.StringValue("eu01"),
+ EncryptionParameters: nil,
},
isValid: true,
},
@@ -58,6 +60,13 @@ func TestMapFields(t *testing.T) {
ProjectId: types.StringValue("pid"),
VolumeId: types.StringValue("nid"),
Region: types.StringValue("eu01"),
+ EncryptionParameters: &encryptionParametersModel{
+ KekKeyId: types.StringValue("kek-key-id"),
+ KekKeyVersion: types.Int64Value(int64(1)),
+ KekKeyringId: types.StringValue("kek-keyring-id"),
+ KeyPayloadBase64: types.StringValue("cm91dGVkb3VidGV2ZXJvdmVyY2xhc3Nkcml2aW5ndGhpbmdmbGFtZWNyb3dkcXVpY2s="),
+ ServiceAccount: types.StringValue("test-sa@sa.stackit.cloud"),
+ },
},
input: &iaas.Volume{
Id: utils.Ptr("nid"),
@@ -71,6 +80,15 @@ func TestMapFields(t *testing.T) {
ServerId: utils.Ptr("sid"),
Size: utils.Ptr(int64(1)),
Source: &iaas.VolumeSource{},
+ Encrypted: utils.Ptr(true),
+ EncryptionParameters: &iaas.VolumeEncryptionParameter{
+ KekKeyId: utils.Ptr("kek-key-id"),
+ KekKeyVersion: utils.Ptr(int64(1)),
+ KekKeyringId: utils.Ptr("kek-keyring-id"),
+ KekProjectId: utils.Ptr("kek-project-id"),
+ KeyPayload: nil,
+ ServiceAccount: utils.Ptr("test-sa@sa.stackit.cloud"),
+ },
},
region: "eu02",
},
@@ -91,12 +109,20 @@ func TestMapFields(t *testing.T) {
"type": types.StringNull(),
"id": types.StringNull(),
}),
- Region: types.StringValue("eu02"),
+ Region: types.StringValue("eu02"),
+ Encrypted: types.BoolValue(true),
+ EncryptionParameters: &encryptionParametersModel{
+ KekKeyId: types.StringValue("kek-key-id"),
+ KekKeyVersion: types.Int64Value(int64(1)),
+ KekKeyringId: types.StringValue("kek-keyring-id"),
+ KeyPayloadBase64: types.StringValue("cm91dGVkb3VidGV2ZXJvdmVyY2xhc3Nkcml2aW5ndGhpbmdmbGFtZWNyb3dkcXVpY2s="),
+ ServiceAccount: types.StringValue("test-sa@sa.stackit.cloud"),
+ },
},
isValid: true,
},
{
- description: "empty_labels",
+ description: "empty labels",
args: args{
state: Model{
ProjectId: types.StringValue("pid"),
@@ -167,8 +193,8 @@ func TestToCreatePayload(t *testing.T) {
isValid bool
}{
{
- "default_ok",
- &Model{
+ description: "no volume encryption",
+ input: &Model{
Name: types.StringValue("name"),
AvailabilityZone: types.StringValue("zone"),
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{
@@ -182,11 +208,11 @@ func TestToCreatePayload(t *testing.T) {
"id": types.StringNull(),
}),
},
- &sourceModel{
+ source: &sourceModel{
Type: types.StringValue("volume"),
Id: types.StringValue("id"),
},
- &iaas.CreateVolumePayload{
+ expected: &iaas.CreateVolumePayload{
Name: utils.Ptr("name"),
AvailabilityZone: utils.Ptr("zone"),
Labels: &map[string]interface{}{
@@ -200,7 +226,81 @@ func TestToCreatePayload(t *testing.T) {
Id: utils.Ptr("id"),
},
},
- true,
+ isValid: true,
+ },
+ {
+ description: "with volume encryption without key payload",
+ input: &Model{
+ Labels: types.MapNull(types.StringType),
+ EncryptionParameters: &encryptionParametersModel{
+ KekKeyId: types.StringValue("kek-key-id"),
+ KekKeyVersion: types.Int64Value(int64(1)),
+ KekKeyringId: types.StringValue("kek-keyring-id"),
+ KeyPayloadBase64: types.StringNull(),
+ ServiceAccount: types.StringValue("test-sa@sa.stackit.cloud"),
+ },
+ },
+ source: &sourceModel{
+ Type: types.StringValue("volume"),
+ Id: types.StringValue("id"),
+ },
+ expected: &iaas.CreateVolumePayload{
+ Source: &iaas.VolumeSource{
+ Type: utils.Ptr("volume"),
+ Id: utils.Ptr("id"),
+ },
+ Labels: &map[string]interface{}{},
+ EncryptionParameters: &iaas.VolumeEncryptionParameter{
+ KekKeyId: utils.Ptr("kek-key-id"),
+ KekKeyVersion: utils.Ptr(int64(1)),
+ KekKeyringId: utils.Ptr("kek-keyring-id"),
+ KekProjectId: nil,
+ KeyPayload: nil,
+ ServiceAccount: utils.Ptr("test-sa@sa.stackit.cloud"),
+ },
+ },
+ isValid: true,
+ },
+ {
+ description: "with volume encryption including key payload",
+ input: &Model{
+ Labels: types.MapNull(types.StringType),
+ EncryptionParameters: &encryptionParametersModel{
+ KekKeyId: types.StringValue("kek-key-id"),
+ KekKeyVersion: types.Int64Value(int64(1)),
+ KekKeyringId: types.StringValue("kek-keyring-id"),
+ KeyPayloadBase64: types.StringValue("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy4="), // The quick brown fox jumps over 13 lazy dogs.
+ ServiceAccount: types.StringValue("test-sa@sa.stackit.cloud"),
+ },
+ },
+ source: &sourceModel{
+ Type: types.StringValue("volume"),
+ Id: types.StringValue("id"),
+ },
+ expected: &iaas.CreateVolumePayload{
+ Source: &iaas.VolumeSource{
+ Type: utils.Ptr("volume"),
+ Id: utils.Ptr("id"),
+ },
+ Labels: &map[string]interface{}{},
+ EncryptionParameters: &iaas.VolumeEncryptionParameter{
+ KekKeyId: utils.Ptr("kek-key-id"),
+ KekKeyVersion: utils.Ptr(int64(1)),
+ KekKeyringId: utils.Ptr("kek-keyring-id"),
+ KekProjectId: nil,
+ KeyPayload: func() *[]byte {
+ keyPayload := []byte{
+ 0x56, 0x47, 0x68, 0x6c, 0x49, 0x48, 0x46, 0x31, 0x61, 0x57, 0x4e, 0x72, 0x49, 0x47, 0x4a,
+ 0x79, 0x62, 0x33, 0x64, 0x75, 0x49, 0x47, 0x5a, 0x76, 0x65, 0x43, 0x42, 0x71, 0x64, 0x57,
+ 0x31, 0x77, 0x63, 0x79, 0x42, 0x76, 0x64, 0x6d, 0x56, 0x79, 0x49, 0x44, 0x45, 0x7a, 0x49,
+ 0x47, 0x78, 0x68, 0x65, 0x6e, 0x6b, 0x67, 0x5a, 0x47, 0x39, 0x6e, 0x63, 0x79, 0x34, 0x3d,
+ }
+ return &keyPayload
+ }(),
+ ServiceAccount: utils.Ptr("test-sa@sa.stackit.cloud"),
+ },
+ },
+ isValid: true,
},
}
for _, tt := range tests {