feat(kms): add keyring resource and datasource (#1049)
relates to STACKITTPR-410
This commit is contained in:
parent
c6e1c3d3a8
commit
edf22a6193
18 changed files with 1157 additions and 9 deletions
|
|
@ -80,9 +80,7 @@ If you want to onboard resources of a STACKIT service `foo` that was not yet in
|
||||||
2. Add a `foo_custom_endpoint` attribute to the provider's `Schema`, in `stackit/provider.go`
|
2. Add a `foo_custom_endpoint` attribute to the provider's `Schema`, in `stackit/provider.go`
|
||||||
3. Check if the custom endpoint is defined and, if yes, use it. In the `Configure` method, add:
|
3. Check if the custom endpoint is defined and, if yes, use it. In the `Configure` method, add:
|
||||||
```go
|
```go
|
||||||
if !(providerConfig.FooCustomEndpoint.IsUnknown() || providerConfig.FooCustomEndpoint.IsNull()) {
|
setStringField(providerConfig.FooCustomEndpoint, func(v string) { providerData.FooCustomEndpoint = v })
|
||||||
providerData.FooCustomEndpoint = providerConfig.FooCustomEndpoint.ValueString()
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
4. Create a utils package, for service `foo` it would be `stackit/internal/foo/utils`. Add a `ConfigureClient()` func and use it in your resource and datasource implementations.
|
4. Create a utils package, for service `foo` it would be `stackit/internal/foo/utils`. Add a `ConfigureClient()` func and use it in your resource and datasource implementations.
|
||||||
|
|
||||||
|
|
|
||||||
38
docs/data-sources/kms_keyring.md
Normal file
38
docs/data-sources/kms_keyring.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||||
|
page_title: "stackit_kms_keyring Data Source - stackit"
|
||||||
|
subcategory: ""
|
||||||
|
description: |-
|
||||||
|
KMS Keyring 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_keyring (Data Source)
|
||||||
|
|
||||||
|
KMS Keyring 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_keyring" "example" {
|
||||||
|
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- schema generated by tfplugindocs -->
|
||||||
|
## Schema
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
- `keyring_id` (String) An auto generated unique id which identifies the keyring.
|
||||||
|
- `project_id` (String) STACKIT project ID to which the keyring is associated.
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
- `region` (String) The resource region. If not defined, the provider region is used.
|
||||||
|
|
||||||
|
### Read-Only
|
||||||
|
|
||||||
|
- `description` (String) A user chosen description to distinguish multiple keyrings.
|
||||||
|
- `display_name` (String) The display name to distinguish multiple keyrings.
|
||||||
|
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`keyring_id`".
|
||||||
|
|
@ -162,6 +162,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
|
||||||
- `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: iam, routing-tables, network
|
- `experiments` (List of String) Enables experiments. These are unstable features without official support. More information can be found in the README. Available Experiments: iam, routing-tables, network
|
||||||
- `git_custom_endpoint` (String) Custom endpoint for the Git service
|
- `git_custom_endpoint` (String) Custom endpoint for the Git service
|
||||||
- `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service
|
- `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service
|
||||||
|
- `kms_custom_endpoint` (String) Custom endpoint for the KMS service
|
||||||
- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
|
- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
|
||||||
- `logme_custom_endpoint` (String) Custom endpoint for the LogMe service
|
- `logme_custom_endpoint` (String) Custom endpoint for the LogMe service
|
||||||
- `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service
|
- `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service
|
||||||
|
|
|
||||||
42
docs/resources/kms_keyring.md
Normal file
42
docs/resources/kms_keyring.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||||
|
page_title: "stackit_kms_keyring Resource - stackit"
|
||||||
|
subcategory: ""
|
||||||
|
description: |-
|
||||||
|
KMS Keyring resource schema. Uses the default_region specified in the provider configuration as a fallback in case no region is defined on resource level.
|
||||||
|
~> Keyrings will not be destroyed by terraform during a terraform destroy. They will just be thrown out of the Terraform state and not deleted on API side. This way we can ensure no keyring setups are deleted by accident and it gives you the option to recover your keys within the grace period.
|
||||||
|
---
|
||||||
|
|
||||||
|
# stackit_kms_keyring (Resource)
|
||||||
|
|
||||||
|
KMS Keyring resource schema. Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on resource level.
|
||||||
|
|
||||||
|
~> Keyrings will **not** be destroyed by terraform during a `terraform destroy`. They will just be thrown out of the Terraform state and not deleted on API side. **This way we can ensure no keyring 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_keyring" "example" {
|
||||||
|
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
display_name = "example name"
|
||||||
|
description = "example description"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- schema generated by tfplugindocs -->
|
||||||
|
## Schema
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
- `display_name` (String) The display name to distinguish multiple keyrings.
|
||||||
|
- `project_id` (String) STACKIT project ID to which the keyring is associated.
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
- `description` (String) A user chosen description to distinguish multiple keyrings.
|
||||||
|
- `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`".
|
||||||
|
- `keyring_id` (String) An auto generated unique id which identifies the keyring.
|
||||||
4
examples/data-sources/stackit_kms_keyring/data-source.tf
Normal file
4
examples/data-sources/stackit_kms_keyring/data-source.tf
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
data "stackit_kms_keyring" "example" {
|
||||||
|
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
}
|
||||||
5
examples/resources/stackit_kms_keyring/resource.tf
Normal file
5
examples/resources/stackit_kms_keyring/resource.tf
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
resource "stackit_kms_keyring" "example" {
|
||||||
|
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||||||
|
display_name = "example name"
|
||||||
|
description = "example description"
|
||||||
|
}
|
||||||
3
go.mod
3
go.mod
|
|
@ -17,6 +17,7 @@ require (
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0
|
github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0
|
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha
|
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha
|
||||||
|
github.com/stackitcloud/stackit-sdk-go/services/kms v1.0.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0
|
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1
|
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1
|
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1
|
||||||
|
|
@ -80,7 +81,7 @@ require (
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/oklog/run v1.1.0 // indirect
|
github.com/oklog/run v1.2.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0
|
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0
|
||||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||||
|
|
|
||||||
6
go.sum
6
go.sum
|
|
@ -137,8 +137,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E=
|
||||||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk=
|
||||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
|
@ -166,6 +166,8 @@ github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0 h1:dnEjyapuv8WwRN5v
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w=
|
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha h1:m1jq6a8dbUe+suFuUNdHmM/cSehpGLUtDbK1CqLqydg=
|
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha h1:m1jq6a8dbUe+suFuUNdHmM/cSehpGLUtDbK1CqLqydg=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha/go.mod h1:Nu1b5Phsv8plgZ51+fkxPVsU91ZJ5Ayz+cthilxdmQ8=
|
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha/go.mod h1:Nu1b5Phsv8plgZ51+fkxPVsU91ZJ5Ayz+cthilxdmQ8=
|
||||||
|
github.com/stackitcloud/stackit-sdk-go/services/kms v1.0.0 h1:zxoOv7Fu+FmdsvTKiKkbmLItrMKfL+QoVtz9ReEF30E=
|
||||||
|
github.com/stackitcloud/stackit-sdk-go/services/kms v1.0.0/go.mod h1:KEPVoO21pC4bjy5l0nyhjUJ0+uVwVWb+k2TYrzJ8xYw=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 h1:q33ZaCBVEBUsnMDxYyuJKtJvGcE5nKgvuPed3s8zXNI=
|
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 h1:q33ZaCBVEBUsnMDxYyuJKtJvGcE5nKgvuPed3s8zXNI=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0/go.mod h1:20QOZ3rBC9wTGgzXzLz9M6YheX0VaxWE0/JI+s8On7k=
|
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0/go.mod h1:20QOZ3rBC9wTGgzXzLz9M6YheX0VaxWE0/JI+s8On7k=
|
||||||
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 h1:hv5WrRU9rN6Jx4OwdOGJRyaQrfA9p1tzEoQK6/CDyoA=
|
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 h1:hv5WrRU9rN6Jx4OwdOGJRyaQrfA9p1tzEoQK6/CDyoA=
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,17 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Separator used for concatenation of TF-internal resource ID
|
|
||||||
const Separator = ","
|
|
||||||
|
|
||||||
type ResourceType string
|
type ResourceType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Resource ResourceType = "resource"
|
Resource ResourceType = "resource"
|
||||||
Datasource ResourceType = "datasource"
|
Datasource ResourceType = "datasource"
|
||||||
|
|
||||||
|
// Separator used for concatenation of TF-internal resource ID
|
||||||
|
Separator = ","
|
||||||
|
|
||||||
|
ResourceRegionFallbackDocstring = "Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on resource level."
|
||||||
|
DatasourceRegionFallbackDocstring = "Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on datasource level."
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProviderData struct {
|
type ProviderData struct {
|
||||||
|
|
@ -33,6 +36,7 @@ type ProviderData struct {
|
||||||
DnsCustomEndpoint string
|
DnsCustomEndpoint string
|
||||||
GitCustomEndpoint string
|
GitCustomEndpoint string
|
||||||
IaaSCustomEndpoint string
|
IaaSCustomEndpoint string
|
||||||
|
KMSCustomEndpoint string
|
||||||
LoadBalancerCustomEndpoint string
|
LoadBalancerCustomEndpoint string
|
||||||
LogMeCustomEndpoint string
|
LogMeCustomEndpoint string
|
||||||
MariaDBCustomEndpoint string
|
MariaDBCustomEndpoint string
|
||||||
|
|
|
||||||
139
stackit/internal/services/kms/keyring/datasource.go
Normal file
139
stackit/internal/services/kms/keyring/datasource.go
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
package kms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
"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 = &keyRingDataSource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewKeyRingDataSource() datasource.DataSource {
|
||||||
|
return &keyRingDataSource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyRingDataSource struct {
|
||||||
|
client *kms.APIClient
|
||||||
|
providerData core.ProviderData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keyRingDataSource) Metadata(_ context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) {
|
||||||
|
response.TypeName = request.ProviderTypeName + "_kms_keyring"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keyRingDataSource) Configure(ctx context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) {
|
||||||
|
var ok bool
|
||||||
|
k.providerData, ok = conversion.ParseProviderData(ctx, request.ProviderData, &response.Diagnostics)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiClient := kmsUtils.ConfigureClient(ctx, &k.providerData, &response.Diagnostics)
|
||||||
|
if response.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
k.client = apiClient
|
||||||
|
tflog.Info(ctx, "KMS client configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *keyRingDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, response *datasource.SchemaResponse) {
|
||||||
|
response.Schema = schema.Schema{
|
||||||
|
Description: fmt.Sprintf("KMS Keyring datasource schema. %s", core.DatasourceRegionFallbackDocstring),
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"description": schema.StringAttribute{
|
||||||
|
Description: "A user chosen description to distinguish multiple keyrings.",
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"display_name": schema.StringAttribute{
|
||||||
|
Description: "The display name to distinguish multiple keyrings.",
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"keyring_id": schema.StringAttribute{
|
||||||
|
Description: "An auto generated unique id which identifies the keyring.",
|
||||||
|
Required: true,
|
||||||
|
Validators: []validator.String{
|
||||||
|
validate.UUID(),
|
||||||
|
validate.NoSeparator(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id": schema.StringAttribute{
|
||||||
|
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`keyring_id`\".",
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"project_id": schema.StringAttribute{
|
||||||
|
Description: "STACKIT project ID to which the keyring 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 *keyRingDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
|
var model Model
|
||||||
|
|
||||||
|
diags := request.Config.Get(ctx, &model)
|
||||||
|
response.Diagnostics.Append(diags...)
|
||||||
|
if response.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
keyRingId := model.KeyRingId.ValueString()
|
||||||
|
region := k.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "keyring_id", keyRingId)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
keyRingResponse, err := k.client.GetKeyRing(ctx, projectId, region, keyRingId).Execute()
|
||||||
|
if err != nil {
|
||||||
|
utils.LogError(
|
||||||
|
ctx,
|
||||||
|
&response.Diagnostics,
|
||||||
|
err,
|
||||||
|
"Reading keyring",
|
||||||
|
fmt.Sprintf("Keyring with ID %q does not exist in project %q.", keyRingId, projectId),
|
||||||
|
map[int]string{
|
||||||
|
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
response.State.RemoveResource(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mapFields(keyRingResponse, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &response.Diagnostics, "Error reading keyring", fmt.Sprintf("Processing API payload: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diags = response.State.Set(ctx, &model)
|
||||||
|
response.Diagnostics.Append(diags...)
|
||||||
|
if response.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tflog.Info(ctx, "Key ring read")
|
||||||
|
}
|
||||||
353
stackit/internal/services/kms/keyring/resource.go
Normal file
353
stackit/internal/services/kms/keyring/resource.go
Normal file
|
|
@ -0,0 +1,353 @@
|
||||||
|
package kms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/services/kms"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/services/kms/wait"
|
||||||
|
"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 = "Keyrings will **not** be destroyed by terraform during a `terraform destroy`. They will just be thrown out of the Terraform state and not deleted on API side. **This way we can ensure no keyring setups are deleted by accident and it gives you the option to recover your keys within the grace period.**"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ resource.Resource = &keyRingResource{}
|
||||||
|
_ resource.ResourceWithConfigure = &keyRingResource{}
|
||||||
|
_ resource.ResourceWithImportState = &keyRingResource{}
|
||||||
|
_ resource.ResourceWithModifyPlan = &keyRingResource{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
Description types.String `tfsdk:"description"`
|
||||||
|
DisplayName types.String `tfsdk:"display_name"`
|
||||||
|
KeyRingId types.String `tfsdk:"keyring_id"`
|
||||||
|
Id types.String `tfsdk:"id"` // needed by TF
|
||||||
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
|
Region types.String `tfsdk:"region"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKeyRingResource() resource.Resource {
|
||||||
|
return &keyRingResource{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyRingResource struct {
|
||||||
|
client *kms.APIClient
|
||||||
|
providerData core.ProviderData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRingResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) {
|
||||||
|
response.TypeName = request.ProviderTypeName + "_kms_keyring"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRingResource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) {
|
||||||
|
var ok bool
|
||||||
|
r.providerData, ok = conversion.ParseProviderData(ctx, request.ProviderData, &response.Diagnostics)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.client = kmsUtils.ConfigureClient(ctx, &r.providerData, &response.Diagnostics)
|
||||||
|
if response.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 *keyRingResource) 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 *keyRingResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) {
|
||||||
|
description := fmt.Sprintf("KMS Keyring resource schema. %s", core.ResourceRegionFallbackDocstring)
|
||||||
|
|
||||||
|
response.Schema = schema.Schema{
|
||||||
|
Description: description,
|
||||||
|
MarkdownDescription: fmt.Sprintf("%s\n\n ~> %s", description, deletionWarning),
|
||||||
|
Attributes: map[string]schema.Attribute{
|
||||||
|
"description": schema.StringAttribute{
|
||||||
|
Description: "A user chosen description to distinguish multiple keyrings.",
|
||||||
|
Optional: true,
|
||||||
|
PlanModifiers: []planmodifier.String{
|
||||||
|
stringplanmodifier.RequiresReplace(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"display_name": schema.StringAttribute{
|
||||||
|
Description: "The display name to distinguish multiple keyrings.",
|
||||||
|
Required: true,
|
||||||
|
PlanModifiers: []planmodifier.String{
|
||||||
|
stringplanmodifier.RequiresReplace(),
|
||||||
|
},
|
||||||
|
Validators: []validator.String{
|
||||||
|
stringvalidator.LengthAtLeast(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"keyring_id": schema.StringAttribute{
|
||||||
|
Description: "An auto generated unique id which identifies the keyring.",
|
||||||
|
Computed: true,
|
||||||
|
PlanModifiers: []planmodifier.String{
|
||||||
|
stringplanmodifier.UseStateForUnknown(),
|
||||||
|
},
|
||||||
|
Validators: []validator.String{
|
||||||
|
validate.UUID(),
|
||||||
|
validate.NoSeparator(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"id": schema.StringAttribute{
|
||||||
|
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`keyring_id`\".",
|
||||||
|
Computed: true,
|
||||||
|
PlanModifiers: []planmodifier.String{
|
||||||
|
stringplanmodifier.UseStateForUnknown(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"project_id": schema.StringAttribute{
|
||||||
|
Description: "STACKIT project ID to which the keyring 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 *keyRingResource) 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)
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
payload, err := toCreatePayload(&model)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating keyring", fmt.Sprintf("Creating API payload: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
createResponse, err := r.client.CreateKeyRing(ctx, projectId, region).CreateKeyRingPayload(*payload).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating keyring", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if createResponse == nil || createResponse.Id == nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating keyring", "API returned empty response")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyRingId := *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,
|
||||||
|
})
|
||||||
|
|
||||||
|
waitResp, err := wait.CreateKeyRingWaitHandler(ctx, r.client, projectId, region, keyRingId).SetSleepBeforeWait(5 * time.Second).WaitWithContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating keyring", fmt.Sprintf("Key Ring creation waiting: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mapFields(waitResp, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating keyring", 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 Ring created")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRingResource) 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)
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "keyring_id", keyRingId)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
keyRingResponse, err := r.client.GetKeyRing(ctx, projectId, region, keyRingId).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 keyring", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mapFields(keyRingResponse, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading keyring", 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 ring read")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRingResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||||
|
// keyrings cannot be updated, so we log an error.
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating keyring", "Keyrings can't be updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRingResource) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// The keyring can't be deleted by Terraform because it potentially has still keys inside it.
|
||||||
|
// These keys might be *scheduled* for deletion, but aren't deleted completely, so the delete request would fail.
|
||||||
|
core.LogAndAddWarning(ctx, &resp.Diagnostics, "Keyring not deleted on API side", deletionWarning)
|
||||||
|
|
||||||
|
tflog.Info(ctx, "keyring deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *keyRingResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||||
|
idParts := strings.Split(req.ID, core.Separator)
|
||||||
|
|
||||||
|
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||||
|
"Error importing keyring",
|
||||||
|
fmt.Sprintf("Exptected import identifier with format: [project_id],[region],[keyring_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],
|
||||||
|
})
|
||||||
|
|
||||||
|
tflog.Info(ctx, "keyring state imported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapFields(keyRing *kms.KeyRing, model *Model, region string) error {
|
||||||
|
if keyRing == nil {
|
||||||
|
return fmt.Errorf("response input is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyRingId string
|
||||||
|
if model.KeyRingId.ValueString() != "" {
|
||||||
|
keyRingId = model.KeyRingId.ValueString()
|
||||||
|
} else if keyRing.Id != nil {
|
||||||
|
keyRingId = *keyRing.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("keyring id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, keyRingId)
|
||||||
|
model.KeyRingId = types.StringValue(keyRingId)
|
||||||
|
model.DisplayName = types.StringPointerValue(keyRing.DisplayName)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
|
||||||
|
// TODO: workaround - remove once STACKITKMS-377 is resolved (just write the return value from the API to the state then)
|
||||||
|
if !(model.Description.IsNull() && keyRing.Description != nil && *keyRing.Description == "") {
|
||||||
|
model.Description = types.StringPointerValue(keyRing.Description)
|
||||||
|
} else {
|
||||||
|
model.Description = types.StringNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCreatePayload(model *Model) (*kms.CreateKeyRingPayload, error) {
|
||||||
|
if model == nil {
|
||||||
|
return nil, fmt.Errorf("nil model")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &kms.CreateKeyRingPayload{
|
||||||
|
Description: conversion.StringValueToPointer(model.Description),
|
||||||
|
DisplayName: conversion.StringValueToPointer(model.DisplayName),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
173
stackit/internal/services/kms/keyring/resource_test.go
Normal file
173
stackit/internal/services/kms/keyring/resource_test.go
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
package kms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testRegion = "eu01"
|
||||||
|
|
||||||
|
func TestMapFields(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
state Model
|
||||||
|
input *kms.KeyRing
|
||||||
|
expected Model
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default values",
|
||||||
|
Model{
|
||||||
|
KeyRingId: types.StringValue("krid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
},
|
||||||
|
&kms.KeyRing{
|
||||||
|
Id: utils.Ptr("krid"),
|
||||||
|
},
|
||||||
|
Model{
|
||||||
|
Description: types.StringNull(),
|
||||||
|
DisplayName: types.StringNull(),
|
||||||
|
KeyRingId: types.StringValue("krid"),
|
||||||
|
Id: types.StringValue("pid,eu01,krid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"values_ok",
|
||||||
|
Model{
|
||||||
|
KeyRingId: types.StringValue("krid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
},
|
||||||
|
&kms.KeyRing{
|
||||||
|
Description: utils.Ptr("descr"),
|
||||||
|
DisplayName: utils.Ptr("name"),
|
||||||
|
Id: utils.Ptr("krid"),
|
||||||
|
},
|
||||||
|
Model{
|
||||||
|
Description: types.StringValue("descr"),
|
||||||
|
DisplayName: types.StringValue("name"),
|
||||||
|
KeyRingId: types.StringValue("krid"),
|
||||||
|
Id: types.StringValue("pid,eu01,krid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_field",
|
||||||
|
Model{},
|
||||||
|
&kms.KeyRing{
|
||||||
|
Id: nil,
|
||||||
|
},
|
||||||
|
Model{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
Model{},
|
||||||
|
nil,
|
||||||
|
Model{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
Model{
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
},
|
||||||
|
&kms.KeyRing{},
|
||||||
|
Model{},
|
||||||
|
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.input, state, testRegion)
|
||||||
|
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 != "" {
|
||||||
|
fmt.Println("state: ", state, " expected: ", tt.expected)
|
||||||
|
t.Fatalf("Data does not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToCreatePayload(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *Model
|
||||||
|
expected *kms.CreateKeyRingPayload
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&Model{},
|
||||||
|
&kms.CreateKeyRingPayload{},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&Model{
|
||||||
|
DisplayName: types.StringValue("name"),
|
||||||
|
},
|
||||||
|
&kms.CreateKeyRingPayload{
|
||||||
|
DisplayName: utils.Ptr("name"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields",
|
||||||
|
&Model{
|
||||||
|
DisplayName: types.StringValue(""),
|
||||||
|
Description: types.StringValue(""),
|
||||||
|
},
|
||||||
|
&kms.CreateKeyRingPayload{
|
||||||
|
DisplayName: utils.Ptr(""),
|
||||||
|
Description: utils.Ptr(""),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_model",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
315
stackit/internal/services/kms/kms_acc_test.go
Normal file
315
stackit/internal/services/kms/kms_acc_test.go
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
package kms_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/plancheck"
|
||||||
|
coreConfig "github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/services/kms"
|
||||||
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/config"
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||||
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed testdata/keyring-min.tf
|
||||||
|
resourceKeyRingMinConfig string
|
||||||
|
|
||||||
|
//go:embed testdata/keyring-max.tf
|
||||||
|
resourceKeyRingMaxConfig string
|
||||||
|
)
|
||||||
|
|
||||||
|
var testConfigKeyRingVarsMin = config.Variables{
|
||||||
|
"project_id": config.StringVariable(testutil.ProjectId),
|
||||||
|
"display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||||
|
}
|
||||||
|
|
||||||
|
var testConfigKeyRingVarsMinUpdated = func() config.Variables {
|
||||||
|
updatedConfig := config.Variables{}
|
||||||
|
maps.Copy(updatedConfig, testConfigKeyRingVarsMin)
|
||||||
|
updatedConfig["display_name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["display_name"])))
|
||||||
|
return updatedConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var testConfigKeyRingVarsMax = config.Variables{
|
||||||
|
"project_id": config.StringVariable(testutil.ProjectId),
|
||||||
|
"description": config.StringVariable("description"),
|
||||||
|
"display_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)),
|
||||||
|
}
|
||||||
|
|
||||||
|
var testConfigKeyRingVarsMaxUpdated = func() config.Variables {
|
||||||
|
updatedConfig := config.Variables{}
|
||||||
|
maps.Copy(updatedConfig, testConfigKeyRingVarsMax)
|
||||||
|
updatedConfig["description"] = config.StringVariable("updated description")
|
||||||
|
updatedConfig["display_name"] = config.StringVariable(fmt.Sprintf("%s-updated", testutil.ConvertConfigVariable(updatedConfig["display_name"])))
|
||||||
|
return updatedConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccKeyRingMin(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||||
|
CheckDestroy: testAccCheckDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
// Creation
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMin,
|
||||||
|
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyRingMinConfig),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionCreate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "project_id", testutil.ProjectId),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "region", testutil.Region),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigKeyRingVarsMin["display_name"])),
|
||||||
|
resource.TestCheckResourceAttrSet("stackit_kms_keyring.keyring", "keyring_id"),
|
||||||
|
resource.TestCheckNoResourceAttr("stackit_kms_keyring.keyring", "description"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Data source
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMin,
|
||||||
|
Config: fmt.Sprintf(`
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
|
||||||
|
data "stackit_kms_keyring" "keyring" {
|
||||||
|
project_id = stackit_kms_keyring.keyring.project_id
|
||||||
|
keyring_id = stackit_kms_keyring.keyring.keyring_id
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
testutil.KMSProviderConfig(), resourceKeyRingMinConfig,
|
||||||
|
),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionNoop),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "project_id", testutil.ProjectId),
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "region", testutil.Region),
|
||||||
|
resource.TestCheckResourceAttrPair(
|
||||||
|
"stackit_kms_keyring.keyring", "keyring_id",
|
||||||
|
"data.stackit_kms_keyring.keyring", "keyring_id",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigKeyRingVarsMin["display_name"])),
|
||||||
|
resource.TestCheckNoResourceAttr("data.stackit_kms_keyring.keyring", "description"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Import
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMin,
|
||||||
|
ResourceName: "stackit_kms_keyring.keyring",
|
||||||
|
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||||
|
r, ok := s.RootModule().Resources["stackit_kms_keyring.keyring"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("couldn't find resource stackit_kms_keyring.keyring")
|
||||||
|
}
|
||||||
|
keyRingId, ok := r.Primary.Attributes["keyring_id"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("couldn't find attribute keyring_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, keyRingId), nil
|
||||||
|
},
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
// Update
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMinUpdated(),
|
||||||
|
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyRingMinConfig),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionReplace),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "project_id", testutil.ProjectId),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "region", testutil.Region),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigKeyRingVarsMinUpdated()["display_name"])),
|
||||||
|
resource.TestCheckResourceAttrSet("stackit_kms_keyring.keyring", "keyring_id"),
|
||||||
|
resource.TestCheckNoResourceAttr("stackit_kms_keyring.keyring", "description"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Deletion is done by the framework implicitly
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccKeyRingMax(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||||
|
CheckDestroy: testAccCheckDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
// Creation
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMax,
|
||||||
|
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyRingMaxConfig),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionCreate),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "project_id", testutil.ProjectId),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "region", testutil.Region),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "description", testutil.ConvertConfigVariable(testConfigKeyRingVarsMax["description"])),
|
||||||
|
resource.TestCheckResourceAttrSet("stackit_kms_keyring.keyring", "keyring_id"),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigKeyRingVarsMax["display_name"])),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Data Source
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMax,
|
||||||
|
Config: fmt.Sprintf(`
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
|
||||||
|
data "stackit_kms_keyring" "keyring" {
|
||||||
|
project_id = stackit_kms_keyring.keyring.project_id
|
||||||
|
keyring_id = stackit_kms_keyring.keyring.keyring_id
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
testutil.KMSProviderConfig(), resourceKeyRingMaxConfig,
|
||||||
|
),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionNoop),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "project_id", testutil.ProjectId),
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "region", testutil.Region),
|
||||||
|
resource.TestCheckResourceAttrPair(
|
||||||
|
"stackit_kms_keyring.keyring", "keyring_id",
|
||||||
|
"data.stackit_kms_keyring.keyring", "keyring_id",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "description", testutil.ConvertConfigVariable(testConfigKeyRingVarsMax["description"])),
|
||||||
|
resource.TestCheckResourceAttr("data.stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigKeyRingVarsMax["display_name"])),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Import
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMax,
|
||||||
|
ResourceName: "stackit_kms_keyring.keyring",
|
||||||
|
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||||
|
r, ok := s.RootModule().Resources["stackit_kms_keyring.keyring"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("couldn't find resource stackit_kms_keyring.keyring")
|
||||||
|
}
|
||||||
|
keyRingId, ok := r.Primary.Attributes["keyring_id"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("couldn't find attribute keyring_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, testutil.Region, keyRingId), nil
|
||||||
|
},
|
||||||
|
ImportState: true,
|
||||||
|
ImportStateVerify: true,
|
||||||
|
},
|
||||||
|
// Update
|
||||||
|
{
|
||||||
|
ConfigVariables: testConfigKeyRingVarsMaxUpdated(),
|
||||||
|
Config: fmt.Sprintf("%s\n%s", testutil.KMSProviderConfig(), resourceKeyRingMaxConfig),
|
||||||
|
ConfigPlanChecks: resource.ConfigPlanChecks{
|
||||||
|
PreApply: []plancheck.PlanCheck{
|
||||||
|
plancheck.ExpectResourceAction("stackit_kms_keyring.keyring", plancheck.ResourceActionReplace),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Check: resource.ComposeAggregateTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "project_id", testutil.ProjectId),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "region", testutil.Region),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "display_name", testutil.ConvertConfigVariable(testConfigKeyRingVarsMaxUpdated()["display_name"])),
|
||||||
|
resource.TestCheckResourceAttrSet("stackit_kms_keyring.keyring", "keyring_id"),
|
||||||
|
resource.TestCheckResourceAttr("stackit_kms_keyring.keyring", "description", testutil.ConvertConfigVariable(testConfigKeyRingVarsMaxUpdated()["description"])),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
// Deletion is done by the framework implicitly
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDestroy(s *terraform.State) error {
|
||||||
|
checkFunctions := []func(s *terraform.State) error{
|
||||||
|
testAccCheckKeyRingDestroy,
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(checkFunctions))
|
||||||
|
|
||||||
|
for _, f := range checkFunctions {
|
||||||
|
go func() {
|
||||||
|
err := f(s)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckKeyRingDestroy(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_keyring" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keyRingId := strings.Split(rs.Primary.ID, core.Separator)[2]
|
||||||
|
err := client.DeleteKeyRingExecute(ctx, testutil.ProjectId, testutil.Region, keyRingId)
|
||||||
|
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 for a keyring which has keys inside it (no matter if
|
||||||
|
// they are scheduled for deletion or not, it will throw an HTTP 400 error and the keyring can't be
|
||||||
|
// deleted then).
|
||||||
|
// But at least we can delete all empty keyrings created by the keyring acc tests this way.
|
||||||
|
if oapiErr.StatusCode == http.StatusBadRequest {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errs = append(errs, fmt.Errorf("cannot trigger keyring deletion %q: %w", keyRingId, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
10
stackit/internal/services/kms/testdata/keyring-max.tf
vendored
Normal file
10
stackit/internal/services/kms/testdata/keyring-max.tf
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
variable "project_id" {}
|
||||||
|
|
||||||
|
variable "display_name" {}
|
||||||
|
variable "description" {}
|
||||||
|
|
||||||
|
resource "stackit_kms_keyring" "keyring" {
|
||||||
|
project_id = var.project_id
|
||||||
|
display_name = var.display_name
|
||||||
|
description = var.description
|
||||||
|
}
|
||||||
8
stackit/internal/services/kms/testdata/keyring-min.tf
vendored
Normal file
8
stackit/internal/services/kms/testdata/keyring-min.tf
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
variable "project_id" {}
|
||||||
|
|
||||||
|
variable "display_name" {}
|
||||||
|
|
||||||
|
resource "stackit_kms_keyring" "keyring" {
|
||||||
|
project_id = var.project_id
|
||||||
|
display_name = var.display_name
|
||||||
|
}
|
||||||
29
stackit/internal/services/kms/utils/util.go
Normal file
29
stackit/internal/services/kms/utils/util.go
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/services/kms"
|
||||||
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||||
|
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConfigureClient(ctx context.Context, providerData *core.ProviderData, diags *diag.Diagnostics) *kms.APIClient {
|
||||||
|
apiClientConfigOptions := []config.ConfigurationOption{
|
||||||
|
config.WithCustomAuth(providerData.RoundTripper),
|
||||||
|
utils.UserAgentConfigOption(providerData.Version),
|
||||||
|
}
|
||||||
|
if providerData.KMSCustomEndpoint != "" {
|
||||||
|
apiClientConfigOptions = append(apiClientConfigOptions, config.WithEndpoint(providerData.KMSCustomEndpoint))
|
||||||
|
}
|
||||||
|
apiClient, err := kms.NewAPIClient(apiClientConfigOptions...)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, diags, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiClient
|
||||||
|
}
|
||||||
|
|
@ -55,6 +55,7 @@ var (
|
||||||
DnsCustomEndpoint = os.Getenv("TF_ACC_DNS_CUSTOM_ENDPOINT")
|
DnsCustomEndpoint = os.Getenv("TF_ACC_DNS_CUSTOM_ENDPOINT")
|
||||||
GitCustomEndpoint = os.Getenv("TF_ACC_GIT_CUSTOM_ENDPOINT")
|
GitCustomEndpoint = os.Getenv("TF_ACC_GIT_CUSTOM_ENDPOINT")
|
||||||
IaaSCustomEndpoint = os.Getenv("TF_ACC_IAAS_CUSTOM_ENDPOINT")
|
IaaSCustomEndpoint = os.Getenv("TF_ACC_IAAS_CUSTOM_ENDPOINT")
|
||||||
|
KMSCustomEndpoint = os.Getenv("TF_ACC_KMS_CUSTOM_ENDPOINT")
|
||||||
LoadBalancerCustomEndpoint = os.Getenv("TF_ACC_LOADBALANCER_CUSTOM_ENDPOINT")
|
LoadBalancerCustomEndpoint = os.Getenv("TF_ACC_LOADBALANCER_CUSTOM_ENDPOINT")
|
||||||
LogMeCustomEndpoint = os.Getenv("TF_ACC_LOGME_CUSTOM_ENDPOINT")
|
LogMeCustomEndpoint = os.Getenv("TF_ACC_LOGME_CUSTOM_ENDPOINT")
|
||||||
MariaDBCustomEndpoint = os.Getenv("TF_ACC_MARIADB_CUSTOM_ENDPOINT")
|
MariaDBCustomEndpoint = os.Getenv("TF_ACC_MARIADB_CUSTOM_ENDPOINT")
|
||||||
|
|
@ -169,6 +170,21 @@ func IaaSProviderConfigWithExperiments() string {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func KMSProviderConfig() string {
|
||||||
|
if KMSCustomEndpoint == "" {
|
||||||
|
return `
|
||||||
|
provider "stackit" {
|
||||||
|
default_region = "eu01"
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
provider "stackit" {
|
||||||
|
kms_custom_endpoint = "%s"
|
||||||
|
}`,
|
||||||
|
KMSCustomEndpoint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func LoadBalancerProviderConfig() string {
|
func LoadBalancerProviderConfig() string {
|
||||||
if LoadBalancerCustomEndpoint == "" {
|
if LoadBalancerCustomEndpoint == "" {
|
||||||
return `
|
return `
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import (
|
||||||
iaasalphaRoutingTableRoutes "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/routingtable/routes"
|
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"
|
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"
|
iaasalphaRoutingTables "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaasalpha/routingtable/tables"
|
||||||
|
kmsKeyRing "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/keyring"
|
||||||
loadBalancer "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/loadbalancer"
|
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"
|
loadBalancerObservabilityCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/observability-credential"
|
||||||
logMeCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/credential"
|
logMeCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/credential"
|
||||||
|
|
@ -131,6 +132,7 @@ type providerModel struct {
|
||||||
DNSCustomEndpoint types.String `tfsdk:"dns_custom_endpoint"`
|
DNSCustomEndpoint types.String `tfsdk:"dns_custom_endpoint"`
|
||||||
GitCustomEndpoint types.String `tfsdk:"git_custom_endpoint"`
|
GitCustomEndpoint types.String `tfsdk:"git_custom_endpoint"`
|
||||||
IaaSCustomEndpoint types.String `tfsdk:"iaas_custom_endpoint"`
|
IaaSCustomEndpoint types.String `tfsdk:"iaas_custom_endpoint"`
|
||||||
|
KMSCustomEndpoint types.String `tfsdk:"kms_custom_endpoint"`
|
||||||
PostgresFlexCustomEndpoint types.String `tfsdk:"postgresflex_custom_endpoint"`
|
PostgresFlexCustomEndpoint types.String `tfsdk:"postgresflex_custom_endpoint"`
|
||||||
MongoDBFlexCustomEndpoint types.String `tfsdk:"mongodbflex_custom_endpoint"`
|
MongoDBFlexCustomEndpoint types.String `tfsdk:"mongodbflex_custom_endpoint"`
|
||||||
ModelServingCustomEndpoint types.String `tfsdk:"modelserving_custom_endpoint"`
|
ModelServingCustomEndpoint types.String `tfsdk:"modelserving_custom_endpoint"`
|
||||||
|
|
@ -173,6 +175,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
||||||
"dns_custom_endpoint": "Custom endpoint for the DNS service",
|
"dns_custom_endpoint": "Custom endpoint for the DNS service",
|
||||||
"git_custom_endpoint": "Custom endpoint for the Git service",
|
"git_custom_endpoint": "Custom endpoint for the Git service",
|
||||||
"iaas_custom_endpoint": "Custom endpoint for the IaaS service",
|
"iaas_custom_endpoint": "Custom endpoint for the IaaS service",
|
||||||
|
"kms_custom_endpoint": "Custom endpoint for the KMS service",
|
||||||
"mongodbflex_custom_endpoint": "Custom endpoint for the MongoDB Flex service",
|
"mongodbflex_custom_endpoint": "Custom endpoint for the MongoDB Flex service",
|
||||||
"modelserving_custom_endpoint": "Custom endpoint for the AI Model Serving service",
|
"modelserving_custom_endpoint": "Custom endpoint for the AI Model Serving service",
|
||||||
"loadbalancer_custom_endpoint": "Custom endpoint for the Load Balancer service",
|
"loadbalancer_custom_endpoint": "Custom endpoint for the Load Balancer service",
|
||||||
|
|
@ -264,6 +267,10 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Description: descriptions["iaas_custom_endpoint"],
|
Description: descriptions["iaas_custom_endpoint"],
|
||||||
},
|
},
|
||||||
|
"kms_custom_endpoint": schema.StringAttribute{
|
||||||
|
Optional: true,
|
||||||
|
Description: descriptions["kms_custom_endpoint"],
|
||||||
|
},
|
||||||
"postgresflex_custom_endpoint": schema.StringAttribute{
|
"postgresflex_custom_endpoint": schema.StringAttribute{
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Description: descriptions["postgresflex_custom_endpoint"],
|
Description: descriptions["postgresflex_custom_endpoint"],
|
||||||
|
|
@ -414,6 +421,7 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
|
||||||
setStringField(providerConfig.GitCustomEndpoint, func(v string) { providerData.GitCustomEndpoint = v })
|
setStringField(providerConfig.GitCustomEndpoint, func(v string) { providerData.GitCustomEndpoint = v })
|
||||||
setStringField(providerConfig.IaaSCustomEndpoint, func(v string) { providerData.IaaSCustomEndpoint = v })
|
setStringField(providerConfig.IaaSCustomEndpoint, func(v string) { providerData.IaaSCustomEndpoint = v })
|
||||||
setStringField(providerConfig.PostgresFlexCustomEndpoint, func(v string) { providerData.PostgresFlexCustomEndpoint = v })
|
setStringField(providerConfig.PostgresFlexCustomEndpoint, func(v string) { providerData.PostgresFlexCustomEndpoint = v })
|
||||||
|
setStringField(providerConfig.KMSCustomEndpoint, func(v string) { providerData.KMSCustomEndpoint = v })
|
||||||
setStringField(providerConfig.ModelServingCustomEndpoint, func(v string) { providerData.ModelServingCustomEndpoint = v })
|
setStringField(providerConfig.ModelServingCustomEndpoint, func(v string) { providerData.ModelServingCustomEndpoint = v })
|
||||||
setStringField(providerConfig.MongoDBFlexCustomEndpoint, func(v string) { providerData.MongoDBFlexCustomEndpoint = v })
|
setStringField(providerConfig.MongoDBFlexCustomEndpoint, func(v string) { providerData.MongoDBFlexCustomEndpoint = v })
|
||||||
setStringField(providerConfig.LoadBalancerCustomEndpoint, func(v string) { providerData.LoadBalancerCustomEndpoint = v })
|
setStringField(providerConfig.LoadBalancerCustomEndpoint, func(v string) { providerData.LoadBalancerCustomEndpoint = v })
|
||||||
|
|
@ -486,6 +494,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
||||||
iaasalphaRoutingTables.NewRoutingTablesDataSource,
|
iaasalphaRoutingTables.NewRoutingTablesDataSource,
|
||||||
iaasalphaRoutingTableRoutes.NewRoutingTableRoutesDataSource,
|
iaasalphaRoutingTableRoutes.NewRoutingTableRoutesDataSource,
|
||||||
iaasSecurityGroupRule.NewSecurityGroupRuleDataSource,
|
iaasSecurityGroupRule.NewSecurityGroupRuleDataSource,
|
||||||
|
kmsKeyRing.NewKeyRingDataSource,
|
||||||
loadBalancer.NewLoadBalancerDataSource,
|
loadBalancer.NewLoadBalancerDataSource,
|
||||||
logMeInstance.NewInstanceDataSource,
|
logMeInstance.NewInstanceDataSource,
|
||||||
logMeCredential.NewCredentialDataSource,
|
logMeCredential.NewCredentialDataSource,
|
||||||
|
|
@ -554,6 +563,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
||||||
iaasSecurityGroupRule.NewSecurityGroupRuleResource,
|
iaasSecurityGroupRule.NewSecurityGroupRuleResource,
|
||||||
iaasalphaRoutingTable.NewRoutingTableResource,
|
iaasalphaRoutingTable.NewRoutingTableResource,
|
||||||
iaasalphaRoutingTableRoute.NewRoutingTableRouteResource,
|
iaasalphaRoutingTableRoute.NewRoutingTableRouteResource,
|
||||||
|
kmsKeyRing.NewKeyRingResource,
|
||||||
loadBalancer.NewLoadBalancerResource,
|
loadBalancer.NewLoadBalancerResource,
|
||||||
loadBalancerObservabilityCredential.NewObservabilityCredentialResource,
|
loadBalancerObservabilityCredential.NewObservabilityCredentialResource,
|
||||||
logMeInstance.NewInstanceResource,
|
logMeInstance.NewInstanceResource,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue