feat(kms): add key resource and datasource (#1055)
relates to STACKITTPR-411
This commit is contained in:
parent
b5f82e7de9
commit
5e8c7a7369
13 changed files with 1369 additions and 3 deletions
45
docs/data-sources/kms_key.md
Normal file
45
docs/data-sources/kms_key.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_kms_key Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
KMS Key datasource schema. Uses the default_region specified in the provider configuration as a fallback in case no region is defined on datasource level.
|
||||
---
|
||||
|
||||
# stackit_kms_key (Data Source)
|
||||
|
||||
KMS Key datasource schema. Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on datasource level.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
data "stackit_kms_key" "key" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `key_id` (String) The ID of the key
|
||||
- `keyring_id` (String) The ID of the associated key ring
|
||||
- `project_id` (String) STACKIT project ID to which the key is associated.
|
||||
|
||||
### Optional
|
||||
|
||||
- `region` (String) The resource region. If not defined, the provider region is used.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `access_scope` (String) The access scope of the key. Default is `PUBLIC`. Possible values are: `PUBLIC`, `SNA`.
|
||||
- `algorithm` (String) The encryption algorithm that the key will use to encrypt data. Possible values are: `aes_256_gcm`, `rsa_2048_oaep_sha256`, `rsa_3072_oaep_sha256`, `rsa_4096_oaep_sha256`, `rsa_4096_oaep_sha512`, `hmac_sha256`, `hmac_sha384`, `hmac_sha512`, `ecdsa_p256_sha256`, `ecdsa_p384_sha384`, `ecdsa_p521_sha512`.
|
||||
- `description` (String) A user chosen description to distinguish multiple keys
|
||||
- `display_name` (String) The display name to distinguish multiple keys
|
||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`keyring_id`,`key_id`".
|
||||
- `import_only` (Boolean) States whether versions can be created or only imported.
|
||||
- `protection` (String) The underlying system that is responsible for protecting the key material. Possible values are: `software`.
|
||||
- `purpose` (String) The purpose for which the key will be used. Possible values are: `symmetric_encrypt_decrypt`, `asymmetric_encrypt_decrypt`, `message_authentication_code`, `asymmetric_sign_verify`.
|
||||
51
docs/resources/kms_key.md
Normal file
51
docs/resources/kms_key.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_kms_key Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
KMS Key resource schema. Uses the default_region specified in the provider configuration as a fallback in case no region is defined on resource level.
|
||||
~> Keys will not be instantly destroyed by terraform during a terraform destroy. They will just be scheduled for deletion via the API and thrown out of the Terraform state afterwards. This way we can ensure no key setups are deleted by accident and it gives you the option to recover your keys within the grace period.
|
||||
---
|
||||
|
||||
# stackit_kms_key (Resource)
|
||||
|
||||
KMS Key resource schema. Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on resource level.
|
||||
|
||||
~> Keys will **not** be instantly destroyed by terraform during a `terraform destroy`. They will just be scheduled for deletion via the API and thrown out of the Terraform state afterwards. **This way we can ensure no key setups are deleted by accident and it gives you the option to recover your keys within the grace period.**
|
||||
|
||||
## Example Usage
|
||||
|
||||
```terraform
|
||||
resource "stackit_kms_key" "key" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
display_name = "key-01"
|
||||
protection = "software"
|
||||
algorithm = "aes_256_gcm"
|
||||
purpose = "symmetric_encrypt_decrypt"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `algorithm` (String) The encryption algorithm that the key will use to encrypt data. Possible values are: `aes_256_gcm`, `rsa_2048_oaep_sha256`, `rsa_3072_oaep_sha256`, `rsa_4096_oaep_sha256`, `rsa_4096_oaep_sha512`, `hmac_sha256`, `hmac_sha384`, `hmac_sha512`, `ecdsa_p256_sha256`, `ecdsa_p384_sha384`, `ecdsa_p521_sha512`.
|
||||
- `display_name` (String) The display name to distinguish multiple keys
|
||||
- `keyring_id` (String) The ID of the associated keyring
|
||||
- `project_id` (String) STACKIT project ID to which the key is associated.
|
||||
- `protection` (String) The underlying system that is responsible for protecting the key material. Possible values are: `software`.
|
||||
- `purpose` (String) The purpose for which the key will be used. Possible values are: `symmetric_encrypt_decrypt`, `asymmetric_encrypt_decrypt`, `message_authentication_code`, `asymmetric_sign_verify`.
|
||||
|
||||
### Optional
|
||||
|
||||
- `access_scope` (String) The access scope of the key. Default is `PUBLIC`. Possible values are: `PUBLIC`, `SNA`.
|
||||
- `description` (String) A user chosen description to distinguish multiple keys
|
||||
- `import_only` (Boolean) States whether versions can be created or only imported.
|
||||
- `region` (String) The resource region. If not defined, the provider region is used.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`keyring_id`,`key_id`".
|
||||
- `key_id` (String) The ID of the key
|
||||
5
examples/data-sources/stackit_kms_key/data-source.tf
Normal file
5
examples/data-sources/stackit_kms_key/data-source.tf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
data "stackit_kms_key" "key" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
}
|
||||
8
examples/resources/stackit_kms_key/resource.tf
Normal file
8
examples/resources/stackit_kms_key/resource.tf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
resource "stackit_kms_key" "key" {
|
||||
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||
display_name = "key-01"
|
||||
protection = "software"
|
||||
algorithm = "aes_256_gcm"
|
||||
purpose = "symmetric_encrypt_decrypt"
|
||||
}
|
||||
2
go.mod
2
go.mod
|
|
@ -11,7 +11,7 @@ require (
|
|||
github.com/hashicorp/terraform-plugin-go v0.29.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.10.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.13.3
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.17.3
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.19.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/cdn v1.6.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/dns v0.17.1
|
||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -152,8 +152,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
|||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.17.3 h1:GsZGmRRc/3GJLmCUnsZswirr5wfLRrwavbnL/renOqg=
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.17.3/go.mod h1:HBCXJGPgdRulplDzhrmwC+Dak9B/x0nzNtmOpu+1Ahg=
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.19.0 h1:dtJcs6/TTCzzb2RKI7HJugDrbCkaFEDmn1pOeFe8qnI=
|
||||
github.com/stackitcloud/stackit-sdk-go/core v0.19.0/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0 h1:7ZKd3b+E/R4TEVShLTXxx5FrsuDuJBOyuVOuKTMa4mo=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0/go.mod h1:/FoXa6hF77Gv8brrvLBCKa5ie1Xy9xn39yfHwaln9Tw=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/cdn v1.6.0 h1:Q+qIdejeMsYMkbtVoI9BpGlKGdSVFRBhH/zj44SP8TM=
|
||||
|
|
|
|||
187
stackit/internal/services/kms/key/datasource.go
Normal file
187
stackit/internal/services/kms/key/datasource.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/kms"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
kmsUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
var (
|
||||
_ datasource.DataSource = &keyDataSource{}
|
||||
)
|
||||
|
||||
func NewKeyDataSource() datasource.DataSource {
|
||||
return &keyDataSource{}
|
||||
}
|
||||
|
||||
type keyDataSource struct {
|
||||
client *kms.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (k *keyDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_kms_key"
|
||||
}
|
||||
|
||||
func (k *keyDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
var ok bool
|
||||
k.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
k.client = kmsUtils.ConfigureClient(ctx, &k.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "KMS client configured")
|
||||
}
|
||||
|
||||
func (k *keyDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
Description: fmt.Sprintf("KMS Key datasource schema. %s", core.DatasourceRegionFallbackDocstring),
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"access_scope": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The access scope of the key. Default is `%s`. %s", string(kms.ACCESSSCOPE_PUBLIC), utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedAccessScopeEnumValues)...)),
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"algorithm": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The encryption algorithm that the key will use to encrypt data. %s", utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedAlgorithmEnumValues)...)),
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Description: "A user chosen description to distinguish multiple keys",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"display_name": schema.StringAttribute{
|
||||
Description: "The display name to distinguish multiple keys",
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`keyring_id`,`key_id`\".",
|
||||
Computed: true,
|
||||
},
|
||||
"import_only": schema.BoolAttribute{
|
||||
Description: "States whether versions can be created or only imported.",
|
||||
Computed: true,
|
||||
},
|
||||
"key_id": schema.StringAttribute{
|
||||
Description: "The ID of the key",
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"keyring_id": schema.StringAttribute{
|
||||
Description: "The ID of the associated key ring",
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"protection": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The underlying system that is responsible for protecting the key material. %s", utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedProtectionEnumValues)...)),
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"purpose": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The purpose for which the key will be used. %s", utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedPurposeEnumValues)...)),
|
||||
Computed: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: "STACKIT project ID to which the key is associated.",
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
// must be computed to allow for storing the override value from the provider
|
||||
Computed: true,
|
||||
Description: "The resource region. If not defined, the provider region is used.",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (k *keyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
keyRingId := model.KeyRingId.ValueString()
|
||||
region := k.providerData.GetRegionWithOverride(model.Region)
|
||||
keyId := model.KeyId.ValueString()
|
||||
|
||||
ctx = tflog.SetField(ctx, "keyring_id", keyRingId)
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "key_id", keyId)
|
||||
|
||||
keyResponse, err := k.client.GetKey(ctx, projectId, region, keyRingId, keyId).Execute()
|
||||
if err != nil {
|
||||
utils.LogError(
|
||||
ctx,
|
||||
&resp.Diagnostics,
|
||||
err,
|
||||
"Reading key",
|
||||
fmt.Sprintf("Key with ID %q does not exist in project %q.", keyId, projectId),
|
||||
map[int]string{
|
||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||
},
|
||||
)
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(keyResponse, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Key read")
|
||||
}
|
||||
443
stackit/internal/services/kms/key/resource.go
Normal file
443
stackit/internal/services/kms/key/resource.go
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
package kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/kms/wait"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/kms"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
kmsUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
)
|
||||
|
||||
const (
|
||||
deletionWarning = "Keys will **not** be instantly destroyed by terraform during a `terraform destroy`. They will just be scheduled for deletion via the API and thrown out of the Terraform state afterwards. **This way we can ensure no key setups are deleted by accident and it gives you the option to recover your keys within the grace period.**"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &keyResource{}
|
||||
_ resource.ResourceWithConfigure = &keyResource{}
|
||||
_ resource.ResourceWithImportState = &keyResource{}
|
||||
_ resource.ResourceWithModifyPlan = &keyResource{}
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
AccessScope types.String `tfsdk:"access_scope"`
|
||||
Algorithm types.String `tfsdk:"algorithm"`
|
||||
Description types.String `tfsdk:"description"`
|
||||
DisplayName types.String `tfsdk:"display_name"`
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
ImportOnly types.Bool `tfsdk:"import_only"`
|
||||
KeyId types.String `tfsdk:"key_id"`
|
||||
KeyRingId types.String `tfsdk:"keyring_id"`
|
||||
Protection types.String `tfsdk:"protection"`
|
||||
Purpose types.String `tfsdk:"purpose"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
}
|
||||
|
||||
func NewKeyResource() resource.Resource {
|
||||
return &keyResource{}
|
||||
}
|
||||
|
||||
type keyResource struct {
|
||||
client *kms.APIClient
|
||||
providerData core.ProviderData
|
||||
}
|
||||
|
||||
func (r *keyResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_kms_key"
|
||||
}
|
||||
|
||||
func (r *keyResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
var ok bool
|
||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
r.client = kmsUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "KMS client configured")
|
||||
}
|
||||
|
||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||
// Use the modifier to set the effective region in the current plan.
|
||||
func (r *keyResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var configModel Model
|
||||
// skip initial empty configuration to avoid follow-up errors
|
||||
if req.Config.Raw.IsNull() {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &configModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
var planModel Model
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
utils.AdaptRegion(ctx, configModel.Region, &planModel.Region, r.providerData.GetRegion(), resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *keyResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
description := fmt.Sprintf("KMS Key resource schema. %s", core.ResourceRegionFallbackDocstring)
|
||||
resp.Schema = schema.Schema{
|
||||
Description: description,
|
||||
MarkdownDescription: fmt.Sprintf("%s\n\n ~> %s", description, deletionWarning),
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"access_scope": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The access scope of the key. Default is `%s`. %s", string(kms.ACCESSSCOPE_PUBLIC), utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedAccessScopeEnumValues)...)),
|
||||
Optional: true,
|
||||
Computed: true, // must be computed because of default value
|
||||
Default: stringdefault.StaticString(string(kms.ACCESSSCOPE_PUBLIC)),
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"algorithm": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The encryption algorithm that the key will use to encrypt data. %s", utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedAlgorithmEnumValues)...)),
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Description: "A user chosen description to distinguish multiple keys",
|
||||
Optional: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"display_name": schema.StringAttribute{
|
||||
Description: "The display name to distinguish multiple keys",
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"id": schema.StringAttribute{
|
||||
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`keyring_id`,`key_id`\".",
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"import_only": schema.BoolAttribute{
|
||||
Description: "States whether versions can be created or only imported.",
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
},
|
||||
"key_id": schema.StringAttribute{
|
||||
Description: "The ID of the key",
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"keyring_id": schema.StringAttribute{
|
||||
Description: "The ID of the associated keyring",
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"protection": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The underlying system that is responsible for protecting the key material. %s", utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedProtectionEnumValues)...)),
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"purpose": schema.StringAttribute{
|
||||
Description: fmt.Sprintf("The purpose for which the key will be used. %s", utils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(kms.AllowedPurposeEnumValues)...)),
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: "STACKIT project ID to which the key is associated.",
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"region": schema.StringAttribute{
|
||||
Optional: true,
|
||||
// must be computed to allow for storing the override value from the provider
|
||||
Computed: true,
|
||||
Description: "The resource region. If not defined, the provider region is used.",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *keyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
keyRingId := model.KeyRingId.ValueString()
|
||||
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "keyring_id", keyRingId)
|
||||
|
||||
payload, err := toCreatePayload(&model)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
createResponse, err := r.client.CreateKey(ctx, projectId, region, keyRingId).CreateKeyPayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if createResponse == nil || createResponse.Id == nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key", "API returned empty response")
|
||||
return
|
||||
}
|
||||
|
||||
keyId := *createResponse.Id
|
||||
// Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler
|
||||
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
|
||||
"project_id": projectId,
|
||||
"region": region,
|
||||
"keyring_id": keyRingId,
|
||||
"key_id": keyId,
|
||||
})
|
||||
|
||||
waitHandlerResp, err := wait.CreateOrUpdateKeyWaitHandler(ctx, r.client, projectId, region, keyRingId, keyId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error waiting for key creation", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(waitHandlerResp, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating key", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Key created")
|
||||
}
|
||||
|
||||
func (r *keyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
keyRingId := model.KeyRingId.ValueString()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
keyId := model.KeyId.ValueString()
|
||||
|
||||
ctx = tflog.SetField(ctx, "keyring_id", keyRingId)
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "region", region)
|
||||
ctx = tflog.SetField(ctx, "key_id", keyId)
|
||||
|
||||
keyResponse, err := r.client.GetKey(ctx, projectId, region, keyRingId, keyId).Execute()
|
||||
if err != nil {
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
ok := errors.As(err, &oapiErr)
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
err = mapFields(keyResponse, &model, region)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading key", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "Key read")
|
||||
}
|
||||
|
||||
func (r *keyResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// keys cannot be updated, so we log an error.
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating key", "Keys can't be updated")
|
||||
}
|
||||
|
||||
func (r *keyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
keyRingId := model.KeyRingId.ValueString()
|
||||
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||
keyId := model.KeyId.ValueString()
|
||||
|
||||
err := r.client.DeleteKey(ctx, projectId, region, keyRingId, keyId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting key", fmt.Sprintf("Calling API: %v", err))
|
||||
}
|
||||
|
||||
// The keys can't be deleted instantly by Terraform, they can only be scheduled for deletion via the API.
|
||||
core.LogAndAddWarning(ctx, &resp.Diagnostics, "Key scheduled for deletion on API side", deletionWarning)
|
||||
|
||||
tflog.Info(ctx, "key deleted")
|
||||
}
|
||||
|
||||
func (r *keyResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 4 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" || idParts[3] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing key",
|
||||
fmt.Sprintf("Exptected import identifier with format: [project_id],[region],[keyring_id],[key_id], got :%q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
|
||||
"project_id": idParts[0],
|
||||
"region": idParts[1],
|
||||
"keyring_id": idParts[2],
|
||||
"key_id": idParts[3],
|
||||
})
|
||||
|
||||
tflog.Info(ctx, "key state imported")
|
||||
}
|
||||
|
||||
func mapFields(key *kms.Key, model *Model, region string) error {
|
||||
if key == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
|
||||
var keyId string
|
||||
if model.KeyId.ValueString() != "" {
|
||||
keyId = model.KeyId.ValueString()
|
||||
} else if key.Id != nil {
|
||||
keyId = *key.Id
|
||||
} else {
|
||||
return fmt.Errorf("key id not present")
|
||||
}
|
||||
|
||||
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, model.KeyRingId.ValueString(), keyId)
|
||||
model.KeyId = types.StringValue(keyId)
|
||||
model.DisplayName = types.StringPointerValue(key.DisplayName)
|
||||
model.Region = types.StringValue(region)
|
||||
model.ImportOnly = types.BoolPointerValue(key.ImportOnly)
|
||||
model.AccessScope = types.StringValue(string(key.GetAccessScope()))
|
||||
model.Algorithm = types.StringValue(string(key.GetAlgorithm()))
|
||||
model.Purpose = types.StringValue(string(key.GetPurpose()))
|
||||
model.Protection = types.StringValue(string(key.GetProtection()))
|
||||
|
||||
// TODO: workaround - remove once STACKITKMS-377 is resolved (just write the return value from the API to the state then)
|
||||
if !(model.Description.IsNull() && key.Description != nil && *key.Description == "") {
|
||||
model.Description = types.StringPointerValue(key.Description)
|
||||
} else {
|
||||
model.Description = types.StringNull()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(model *Model) (*kms.CreateKeyPayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
return &kms.CreateKeyPayload{
|
||||
AccessScope: kms.CreateKeyPayloadGetAccessScopeAttributeType(conversion.StringValueToPointer(model.AccessScope)),
|
||||
Algorithm: kms.CreateKeyPayloadGetAlgorithmAttributeType(conversion.StringValueToPointer(model.Algorithm)),
|
||||
Protection: kms.CreateKeyPayloadGetProtectionAttributeType(conversion.StringValueToPointer(model.Protection)),
|
||||
Description: conversion.StringValueToPointer(model.Description),
|
||||
DisplayName: conversion.StringValueToPointer(model.DisplayName),
|
||||
ImportOnly: conversion.BoolValueToPointer(model.ImportOnly),
|
||||
Purpose: kms.CreateKeyPayloadGetPurposeAttributeType(conversion.StringValueToPointer(model.Purpose)),
|
||||
}, nil
|
||||
}
|
||||
216
stackit/internal/services/kms/key/resource_test.go
Normal file
216
stackit/internal/services/kms/key/resource_test.go
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
package kms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/kms"
|
||||
)
|
||||
|
||||
var (
|
||||
keyId = uuid.NewString()
|
||||
keyRingId = uuid.NewString()
|
||||
projectId = uuid.NewString()
|
||||
)
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
type args struct {
|
||||
state Model
|
||||
input *kms.Key
|
||||
region string
|
||||
}
|
||||
tests := []struct {
|
||||
description string
|
||||
args args
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
description: "default values",
|
||||
args: args{
|
||||
state: Model{
|
||||
KeyId: types.StringValue(keyId),
|
||||
KeyRingId: types.StringValue(keyRingId),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
input: &kms.Key{
|
||||
Id: utils.Ptr(keyId),
|
||||
Protection: utils.Ptr(kms.PROTECTION_SOFTWARE),
|
||||
Algorithm: utils.Ptr(kms.ALGORITHM_ECDSA_P256_SHA256),
|
||||
Purpose: utils.Ptr(kms.PURPOSE_ASYMMETRIC_SIGN_VERIFY),
|
||||
AccessScope: utils.Ptr(kms.ACCESSSCOPE_PUBLIC),
|
||||
},
|
||||
region: "eu01",
|
||||
},
|
||||
expected: Model{
|
||||
Description: types.StringNull(),
|
||||
DisplayName: types.StringNull(),
|
||||
KeyRingId: types.StringValue(keyRingId),
|
||||
KeyId: types.StringValue(keyId),
|
||||
Id: types.StringValue(fmt.Sprintf("%s,eu01,%s,%s", projectId, keyRingId, keyId)),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
Region: types.StringValue("eu01"),
|
||||
Protection: types.StringValue(string(kms.PROTECTION_SOFTWARE)),
|
||||
Algorithm: types.StringValue(string(kms.ALGORITHM_ECDSA_P256_SHA256)),
|
||||
Purpose: types.StringValue(string(kms.PURPOSE_ASYMMETRIC_SIGN_VERIFY)),
|
||||
AccessScope: types.StringValue(string(kms.ACCESSSCOPE_PUBLIC)),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "values_ok",
|
||||
args: args{
|
||||
state: Model{
|
||||
KeyId: types.StringValue(keyId),
|
||||
KeyRingId: types.StringValue(keyRingId),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
input: &kms.Key{
|
||||
Id: utils.Ptr(keyId),
|
||||
Description: utils.Ptr("descr"),
|
||||
DisplayName: utils.Ptr("name"),
|
||||
ImportOnly: utils.Ptr(true),
|
||||
Protection: utils.Ptr(kms.PROTECTION_SOFTWARE),
|
||||
Algorithm: utils.Ptr(kms.ALGORITHM_AES_256_GCM),
|
||||
Purpose: utils.Ptr(kms.PURPOSE_MESSAGE_AUTHENTICATION_CODE),
|
||||
AccessScope: utils.Ptr(kms.ACCESSSCOPE_SNA),
|
||||
},
|
||||
region: "eu01",
|
||||
},
|
||||
expected: Model{
|
||||
Description: types.StringValue("descr"),
|
||||
DisplayName: types.StringValue("name"),
|
||||
KeyId: types.StringValue(keyId),
|
||||
KeyRingId: types.StringValue(keyRingId),
|
||||
Id: types.StringValue(fmt.Sprintf("%s,eu01,%s,%s", projectId, keyRingId, keyId)),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
Region: types.StringValue("eu01"),
|
||||
ImportOnly: types.BoolValue(true),
|
||||
Protection: types.StringValue(string(kms.PROTECTION_SOFTWARE)),
|
||||
Algorithm: types.StringValue(string(kms.ALGORITHM_AES_256_GCM)),
|
||||
Purpose: types.StringValue(string(kms.PURPOSE_MESSAGE_AUTHENTICATION_CODE)),
|
||||
AccessScope: types.StringValue(string(kms.ACCESSSCOPE_SNA)),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "nil_response_field",
|
||||
args: args{
|
||||
state: Model{},
|
||||
input: &kms.Key{
|
||||
Id: nil,
|
||||
},
|
||||
},
|
||||
expected: Model{},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
description: "nil_response",
|
||||
args: args{
|
||||
state: Model{},
|
||||
input: nil,
|
||||
},
|
||||
expected: Model{},
|
||||
isValid: false,
|
||||
},
|
||||
{
|
||||
description: "no_resource_id",
|
||||
args: args{
|
||||
state: Model{
|
||||
Region: types.StringValue("eu01"),
|
||||
ProjectId: types.StringValue(projectId),
|
||||
},
|
||||
input: &kms.Key{},
|
||||
},
|
||||
expected: Model{},
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
state := &Model{
|
||||
ProjectId: tt.expected.ProjectId,
|
||||
KeyRingId: tt.expected.KeyRingId,
|
||||
}
|
||||
err := mapFields(tt.args.input, 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(state, &tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
expected *kms.CreateKeyPayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
description: "default_values",
|
||||
input: &Model{},
|
||||
expected: &kms.CreateKeyPayload{},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "simple_values",
|
||||
input: &Model{
|
||||
DisplayName: types.StringValue("name"),
|
||||
},
|
||||
expected: &kms.CreateKeyPayload{
|
||||
DisplayName: utils.Ptr("name"),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "null_fields",
|
||||
input: &Model{
|
||||
DisplayName: types.StringValue(""),
|
||||
Description: types.StringValue(""),
|
||||
},
|
||||
expected: &kms.CreateKeyPayload{
|
||||
DisplayName: utils.Ptr(""),
|
||||
Description: utils.Ptr(""),
|
||||
},
|
||||
isValid: true,
|
||||
},
|
||||
{
|
||||
description: "nil_model",
|
||||
input: nil,
|
||||
expected: nil,
|
||||
isValid: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output, err := toCreatePayload(tt.input)
|
||||
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(output, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -30,8 +30,16 @@ var (
|
|||
|
||||
//go:embed testdata/keyring-max.tf
|
||||
resourceKeyRingMaxConfig string
|
||||
|
||||
//go:embed testdata/key-min.tf
|
||||
resourceKeyMinConfig string
|
||||
|
||||
//go:embed testdata/key-max.tf
|
||||
resourceKeyMaxConfig string
|
||||
)
|
||||
|
||||
// KEY RING - MIN
|
||||
|
||||
var testConfigKeyRingVarsMin = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||
|
|
@ -44,6 +52,8 @@ var testConfigKeyRingVarsMinUpdated = func() config.Variables {
|
|||
return updatedConfig
|
||||
}
|
||||
|
||||
// KEY RING - MAX
|
||||
|
||||
var testConfigKeyRingVarsMax = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"description": config.StringVariable("description"),
|
||||
|
|
@ -58,6 +68,51 @@ var testConfigKeyRingVarsMaxUpdated = func() config.Variables {
|
|||
return updatedConfig
|
||||
}
|
||||
|
||||
// KEY - MIN
|
||||
|
||||
var testConfigKeyVarsMin = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"keyring_display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||
"display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||
"algorithm": config.StringVariable(string(kms.ALGORITHM_AES_256_GCM)),
|
||||
"protection": config.StringVariable("software"),
|
||||
"purpose": config.StringVariable(string(kms.PURPOSE_SYMMETRIC_ENCRYPT_DECRYPT)),
|
||||
}
|
||||
|
||||
var testConfigKeyVarsMinUpdated = func() config.Variables {
|
||||
updatedConfig := config.Variables{}
|
||||
maps.Copy(updatedConfig, testConfigKeyVarsMin)
|
||||
updatedConfig["display_name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["display_name"])))
|
||||
updatedConfig["algorithm"] = config.StringVariable(string(kms.ALGORITHM_RSA_3072_OAEP_SHA256))
|
||||
updatedConfig["purpose"] = config.StringVariable(string(kms.PURPOSE_ASYMMETRIC_ENCRYPT_DECRYPT))
|
||||
return updatedConfig
|
||||
}
|
||||
|
||||
// KEY - MAX
|
||||
|
||||
var testConfigKeyVarsMax = config.Variables{
|
||||
"project_id": config.StringVariable(testutil.ProjectId),
|
||||
"keyring_display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||
"display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||
"algorithm": config.StringVariable(string(kms.ALGORITHM_AES_256_GCM)),
|
||||
"protection": config.StringVariable("software"),
|
||||
"purpose": config.StringVariable(string(kms.PURPOSE_SYMMETRIC_ENCRYPT_DECRYPT)),
|
||||
"access_scope": config.StringVariable(string(kms.ACCESSSCOPE_PUBLIC)),
|
||||
"import_only": config.BoolVariable(true),
|
||||
"description": config.StringVariable("kms-key-description"),
|
||||
}
|
||||
|
||||
var testConfigKeyVarsMaxUpdated = func() config.Variables {
|
||||
updatedConfig := config.Variables{}
|
||||
maps.Copy(updatedConfig, testConfigKeyVarsMax)
|
||||
updatedConfig["display_name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["display_name"])))
|
||||
updatedConfig["algorithm"] = config.StringVariable(string(kms.ALGORITHM_RSA_3072_OAEP_SHA256))
|
||||
updatedConfig["purpose"] = config.StringVariable(string(kms.PURPOSE_ASYMMETRIC_ENCRYPT_DECRYPT))
|
||||
updatedConfig["import_only"] = config.BoolVariable(true)
|
||||
updatedConfig["description"] = config.StringVariable("kms-key-description-updated")
|
||||
return updatedConfig
|
||||
}
|
||||
|
||||
func TestAccKeyRingMin(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
|
|
@ -246,8 +301,269 @@ func TestAccKeyRingMax(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccKeyMin(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMin,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyMinConfig),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionCreate),
|
||||
plancheck.ExpectResourceAction("stackit_kms_key.key", plancheck.ResourceActionCreate),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "region", testutil.Region),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_keyring.keyring", "keyring_id",
|
||||
"stackit_kms_key.key", "keyring_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_kms_key.key", "key_id"),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "algorithm", testutil.ConvertConfigVariable(testConfigKeyVarsMin["algorithm"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "display_name", testutil.ConvertConfigVariable(testConfigKeyVarsMin["display_name"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "purpose", testutil.ConvertConfigVariable(testConfigKeyVarsMin["purpose"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "protection", testutil.ConvertConfigVariable(testConfigKeyVarsMin["protection"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_kms_key.key", "description"),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "access_scope", string(kms.ACCESSSCOPE_PUBLIC)),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "import_only", "false"),
|
||||
),
|
||||
},
|
||||
// Data Source
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMin,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_kms_key" "key" {
|
||||
project_id = stackit_kms_key.key.project_id
|
||||
keyring_id = stackit_kms_key.key.keyring_id
|
||||
key_id = stackit_kms_key.key.key_id
|
||||
}
|
||||
`,
|
||||
testutil.KMSProviderConfig(), resourceKeyMinConfig,
|
||||
),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionNoop),
|
||||
plancheck.ExpectResourceAction("stackit_kms_key.key", plancheck.ResourceActionNoop),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "region", testutil.Region),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_keyring.keyring", "keyring_id",
|
||||
"data.stackit_kms_key.key", "keyring_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_key.key", "key_id",
|
||||
"data.stackit_kms_key.key", "key_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "algorithm", testutil.ConvertConfigVariable(testConfigKeyVarsMin["algorithm"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "display_name", testutil.ConvertConfigVariable(testConfigKeyVarsMin["display_name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "purpose", testutil.ConvertConfigVariable(testConfigKeyVarsMin["purpose"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "protection", testutil.ConvertConfigVariable(testConfigKeyVarsMin["protection"])),
|
||||
resource.TestCheckNoResourceAttr("data.stackit_kms_key.key", "description"),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "access_scope", string(kms.ACCESSSCOPE_PUBLIC)),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "import_only", "false"),
|
||||
),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMin,
|
||||
ResourceName: "stackit_kms_key.key",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_kms_key.key"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_kms_key.key")
|
||||
}
|
||||
keyRingId, ok := r.Primary.Attributes["keyring_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute keyring_id")
|
||||
}
|
||||
keyId, ok := r.Primary.Attributes["key_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute key_id")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, keyRingId, keyId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMinUpdated(),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyMinConfig),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionNoop),
|
||||
plancheck.ExpectResourceAction("stackit_kms_key.key", plancheck.ResourceActionReplace),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "region", testutil.Region),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_keyring.keyring", "keyring_id",
|
||||
"stackit_kms_key.key", "keyring_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_kms_key.key", "key_id"),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "algorithm", testutil.ConvertConfigVariable(testConfigKeyVarsMinUpdated()["algorithm"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "display_name", testutil.ConvertConfigVariable(testConfigKeyVarsMinUpdated()["display_name"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "purpose", testutil.ConvertConfigVariable(testConfigKeyVarsMinUpdated()["purpose"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "protection", testutil.ConvertConfigVariable(testConfigKeyVarsMinUpdated()["protection"])),
|
||||
resource.TestCheckNoResourceAttr("stackit_kms_key.key", "description"),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "access_scope", string(kms.ACCESSSCOPE_PUBLIC)),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "import_only", "false"),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccKeyMax(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccCheckDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMax,
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyMaxConfig),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionCreate),
|
||||
plancheck.ExpectResourceAction("stackit_kms_key.key", plancheck.ResourceActionCreate),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "region", testutil.Region),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_keyring.keyring", "keyring_id",
|
||||
"stackit_kms_key.key", "keyring_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_kms_key.key", "key_id"),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "algorithm", testutil.ConvertConfigVariable(testConfigKeyVarsMax["algorithm"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "display_name", testutil.ConvertConfigVariable(testConfigKeyVarsMax["display_name"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "purpose", testutil.ConvertConfigVariable(testConfigKeyVarsMax["purpose"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "protection", testutil.ConvertConfigVariable(testConfigKeyVarsMax["protection"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "description", testutil.ConvertConfigVariable(testConfigKeyVarsMax["description"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "access_scope", testutil.ConvertConfigVariable(testConfigKeyVarsMax["access_scope"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "import_only", testutil.ConvertConfigVariable(testConfigKeyVarsMax["import_only"])),
|
||||
),
|
||||
},
|
||||
// Data Source
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMax,
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
%s
|
||||
|
||||
data "stackit_kms_key" "key" {
|
||||
project_id = stackit_kms_key.key.project_id
|
||||
keyring_id = stackit_kms_key.key.keyring_id
|
||||
key_id = stackit_kms_key.key.key_id
|
||||
}
|
||||
`,
|
||||
testutil.KMSProviderConfig(), resourceKeyMaxConfig,
|
||||
),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionNoop),
|
||||
plancheck.ExpectResourceAction("stackit_kms_key.key", plancheck.ResourceActionNoop),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "region", testutil.Region),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_keyring.keyring", "keyring_id",
|
||||
"data.stackit_kms_key.key", "keyring_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_key.key", "key_id",
|
||||
"data.stackit_kms_key.key", "key_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "algorithm", testutil.ConvertConfigVariable(testConfigKeyVarsMax["algorithm"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "display_name", testutil.ConvertConfigVariable(testConfigKeyVarsMax["display_name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "purpose", testutil.ConvertConfigVariable(testConfigKeyVarsMax["purpose"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "protection", testutil.ConvertConfigVariable(testConfigKeyVarsMax["protection"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "description", testutil.ConvertConfigVariable(testConfigKeyVarsMax["description"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "access_scope", testutil.ConvertConfigVariable(testConfigKeyVarsMax["access_scope"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_kms_key.key", "import_only", testutil.ConvertConfigVariable(testConfigKeyVarsMax["import_only"])),
|
||||
),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMax,
|
||||
ResourceName: "stackit_kms_key.key",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_kms_key.key"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_kms_key.key")
|
||||
}
|
||||
keyRingId, ok := r.Primary.Attributes["keyring_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute keyring_id")
|
||||
}
|
||||
keyId, ok := r.Primary.Attributes["key_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute key_id")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s,%s,%s", testutil.ProjectId, testutil.Region, keyRingId, keyId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
// Update
|
||||
{
|
||||
ConfigVariables: testConfigKeyVarsMaxUpdated(),
|
||||
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyMaxConfig),
|
||||
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||
PreApply: []plancheck.PlanCheck{
|
||||
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionNoop),
|
||||
plancheck.ExpectResourceAction("stackit_kms_key.key", plancheck.ResourceActionReplace),
|
||||
},
|
||||
},
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "project_id", testutil.ProjectId),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "region", testutil.Region),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"stackit_kms_keyring.keyring", "keyring_id",
|
||||
"stackit_kms_key.key", "keyring_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrSet("stackit_kms_key.key", "key_id"),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "algorithm", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["algorithm"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "display_name", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["display_name"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "purpose", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["purpose"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "protection", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["protection"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "description", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["description"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "access_scope", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["access_scope"])),
|
||||
resource.TestCheckResourceAttr("stackit_kms_key.key", "import_only", testutil.ConvertConfigVariable(testConfigKeyVarsMaxUpdated()["import_only"])),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckDestroy(s *terraform.State) error {
|
||||
checkFunctions := []func(s *terraform.State) error{
|
||||
testAccCheckKeyDestroy,
|
||||
testAccCheckKeyRingDestroy,
|
||||
}
|
||||
|
||||
|
|
@ -313,3 +629,47 @@ func testAccCheckKeyRingDestroy(s *terraform.State) error {
|
|||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func testAccCheckKeyDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *kms.APIClient
|
||||
var err error
|
||||
if testutil.KMSCustomEndpoint == "" {
|
||||
client, err = kms.NewAPIClient()
|
||||
} else {
|
||||
client, err = kms.NewAPIClient(
|
||||
coreConfig.WithEndpoint(testutil.KMSCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "stackit_kms_key" {
|
||||
continue
|
||||
}
|
||||
keyRingId := strings.Split(rs.Primary.ID, core.Separator)[2]
|
||||
keyId := strings.Split(rs.Primary.ID, core.Separator)[3]
|
||||
err := client.DeleteKeyExecute(ctx, testutil.ProjectId, testutil.Region, keyRingId, keyId)
|
||||
if err != nil {
|
||||
var oapiErr *oapierror.GenericOpenAPIError
|
||||
if errors.As(err, &oapiErr) {
|
||||
if oapiErr.StatusCode == http.StatusNotFound {
|
||||
continue
|
||||
}
|
||||
|
||||
// workaround: when the delete endpoint is called a second time for a key which is already scheduled
|
||||
// for deletion, one will get an HTTP 400 error which we have to ignore here
|
||||
if oapiErr.StatusCode == http.StatusBadRequest {
|
||||
continue
|
||||
}
|
||||
}
|
||||
errs = append(errs, fmt.Errorf("cannot trigger key deletion %q: %w", keyRingId, err))
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
|
|
|||
27
stackit/internal/services/kms/testdata/key-max.tf
vendored
Normal file
27
stackit/internal/services/kms/testdata/key-max.tf
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
variable "project_id" {}
|
||||
|
||||
variable "keyring_display_name" {}
|
||||
variable "display_name" {}
|
||||
variable "description" {}
|
||||
variable "access_scope" {}
|
||||
variable "import_only" {}
|
||||
variable "protection" {}
|
||||
variable "algorithm" {}
|
||||
variable "purpose" {}
|
||||
|
||||
resource "stackit_kms_keyring" "keyring" {
|
||||
project_id = var.project_id
|
||||
display_name = var.keyring_display_name
|
||||
}
|
||||
|
||||
resource "stackit_kms_key" "key" {
|
||||
project_id = var.project_id
|
||||
keyring_id = stackit_kms_keyring.keyring.keyring_id
|
||||
protection = var.protection
|
||||
algorithm = var.algorithm
|
||||
display_name = var.display_name
|
||||
purpose = var.purpose
|
||||
description = var.description
|
||||
access_scope = var.access_scope
|
||||
import_only = var.import_only
|
||||
}
|
||||
21
stackit/internal/services/kms/testdata/key-min.tf
vendored
Normal file
21
stackit/internal/services/kms/testdata/key-min.tf
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
variable "project_id" {}
|
||||
|
||||
variable "keyring_display_name" {}
|
||||
variable "display_name" {}
|
||||
variable "protection" {}
|
||||
variable "algorithm" {}
|
||||
variable "purpose" {}
|
||||
|
||||
resource "stackit_kms_keyring" "keyring" {
|
||||
project_id = var.project_id
|
||||
display_name = var.keyring_display_name
|
||||
}
|
||||
|
||||
resource "stackit_kms_key" "key" {
|
||||
project_id = var.project_id
|
||||
keyring_id = stackit_kms_keyring.keyring.keyring_id
|
||||
protection = var.protection
|
||||
algorithm = var.algorithm
|
||||
display_name = var.display_name
|
||||
purpose = var.purpose
|
||||
}
|
||||
|
|
@ -48,6 +48,7 @@ import (
|
|||
iaasalphaRoutingTableRoutes "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/routingtable/routes"
|
||||
iaasalphaRoutingTable "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/routingtable/table"
|
||||
iaasalphaRoutingTables "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/routingtable/tables"
|
||||
kmsKey "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/key"
|
||||
kmsKeyRing "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/keyring"
|
||||
loadBalancer "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/loadbalancer"
|
||||
loadBalancerObservabilityCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/observability-credential"
|
||||
|
|
@ -494,6 +495,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
iaasalphaRoutingTables.NewRoutingTablesDataSource,
|
||||
iaasalphaRoutingTableRoutes.NewRoutingTableRoutesDataSource,
|
||||
iaasSecurityGroupRule.NewSecurityGroupRuleDataSource,
|
||||
kmsKey.NewKeyDataSource,
|
||||
kmsKeyRing.NewKeyRingDataSource,
|
||||
loadBalancer.NewLoadBalancerDataSource,
|
||||
logMeInstance.NewInstanceDataSource,
|
||||
|
|
@ -563,6 +565,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
|||
iaasSecurityGroupRule.NewSecurityGroupRuleResource,
|
||||
iaasalphaRoutingTable.NewRoutingTableResource,
|
||||
iaasalphaRoutingTableRoute.NewRoutingTableRouteResource,
|
||||
kmsKey.NewKeyResource,
|
||||
kmsKeyRing.NewKeyRingResource,
|
||||
loadBalancer.NewLoadBalancerResource,
|
||||
loadBalancerObservabilityCredential.NewObservabilityCredentialResource,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue