Skip to content

Commit edf22a6

Browse files
authored
feat(kms): add keyring resource and datasource (#1049)
relates to STACKITTPR-410
1 parent c6e1c3d commit edf22a6

File tree

18 files changed

+1157
-9
lines changed

18 files changed

+1157
-9
lines changed

CONTRIBUTION.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ If you want to onboard resources of a STACKIT service `foo` that was not yet in
8080
2. Add a `foo_custom_endpoint` attribute to the provider's `Schema`, in `stackit/provider.go`
8181
3. Check if the custom endpoint is defined and, if yes, use it. In the `Configure` method, add:
8282
```go
83-
if !(providerConfig.FooCustomEndpoint.IsUnknown() || providerConfig.FooCustomEndpoint.IsNull()) {
84-
providerData.FooCustomEndpoint = providerConfig.FooCustomEndpoint.ValueString()
85-
}
83+
setStringField(providerConfig.FooCustomEndpoint, func(v string) { providerData.FooCustomEndpoint = v })
8684
```
8785
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.
8886

docs/data-sources/kms_keyring.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_kms_keyring Data Source - stackit"
4+
subcategory: ""
5+
description: |-
6+
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.
7+
---
8+
9+
# stackit_kms_keyring (Data Source)
10+
11+
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.
12+
13+
## Example Usage
14+
15+
```terraform
16+
data "stackit_kms_keyring" "example" {
17+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
18+
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
19+
}
20+
```
21+
22+
<!-- schema generated by tfplugindocs -->
23+
## Schema
24+
25+
### Required
26+
27+
- `keyring_id` (String) An auto generated unique id which identifies the keyring.
28+
- `project_id` (String) STACKIT project ID to which the keyring is associated.
29+
30+
### Optional
31+
32+
- `region` (String) The resource region. If not defined, the provider region is used.
33+
34+
### Read-Only
35+
36+
- `description` (String) A user chosen description to distinguish multiple keyrings.
37+
- `display_name` (String) The display name to distinguish multiple keyrings.
38+
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`keyring_id`".

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
162162
- `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
163163
- `git_custom_endpoint` (String) Custom endpoint for the Git service
164164
- `iaas_custom_endpoint` (String) Custom endpoint for the IaaS service
165+
- `kms_custom_endpoint` (String) Custom endpoint for the KMS service
165166
- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service
166167
- `logme_custom_endpoint` (String) Custom endpoint for the LogMe service
167168
- `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service

docs/resources/kms_keyring.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_kms_keyring Resource - stackit"
4+
subcategory: ""
5+
description: |-
6+
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.
7+
~> 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.
8+
---
9+
10+
# stackit_kms_keyring (Resource)
11+
12+
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.
13+
14+
~> 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.**
15+
16+
## Example Usage
17+
18+
```terraform
19+
resource "stackit_kms_keyring" "example" {
20+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
21+
display_name = "example name"
22+
description = "example description"
23+
}
24+
```
25+
26+
<!-- schema generated by tfplugindocs -->
27+
## Schema
28+
29+
### Required
30+
31+
- `display_name` (String) The display name to distinguish multiple keyrings.
32+
- `project_id` (String) STACKIT project ID to which the keyring is associated.
33+
34+
### Optional
35+
36+
- `description` (String) A user chosen description to distinguish multiple keyrings.
37+
- `region` (String) The resource region. If not defined, the provider region is used.
38+
39+
### Read-Only
40+
41+
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`keyring_id`".
42+
- `keyring_id` (String) An auto generated unique id which identifies the keyring.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
data "stackit_kms_keyring" "example" {
2+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
keyring_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "stackit_kms_keyring" "example" {
2+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
display_name = "example name"
4+
description = "example description"
5+
}

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ require (
1717
github.com/stackitcloud/stackit-sdk-go/services/git v0.8.0
1818
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0
1919
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha
20+
github.com/stackitcloud/stackit-sdk-go/services/kms v1.0.0
2021
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0
2122
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1
2223
github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.1
@@ -80,7 +81,7 @@ require (
8081
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
8182
github.com/mitchellh/mapstructure v1.5.0 // indirect
8283
github.com/mitchellh/reflectwalk v1.0.2 // indirect
83-
github.com/oklog/run v1.1.0 // indirect
84+
github.com/oklog/run v1.2.0 // indirect
8485
github.com/rogpeppe/go-internal v1.13.1 // indirect
8586
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.9.0
8687
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
137137
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
138138
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
139139
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
140-
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
141-
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
140+
github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E=
141+
github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk=
142142
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
143143
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
144144
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
166166
github.com/stackitcloud/stackit-sdk-go/services/iaas v0.31.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w=
167167
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha h1:m1jq6a8dbUe+suFuUNdHmM/cSehpGLUtDbK1CqLqydg=
168168
github.com/stackitcloud/stackit-sdk-go/services/iaasalpha v0.1.21-alpha/go.mod h1:Nu1b5Phsv8plgZ51+fkxPVsU91ZJ5Ayz+cthilxdmQ8=
169+
github.com/stackitcloud/stackit-sdk-go/services/kms v1.0.0 h1:zxoOv7Fu+FmdsvTKiKkbmLItrMKfL+QoVtz9ReEF30E=
170+
github.com/stackitcloud/stackit-sdk-go/services/kms v1.0.0/go.mod h1:KEPVoO21pC4bjy5l0nyhjUJ0+uVwVWb+k2TYrzJ8xYw=
169171
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0 h1:q33ZaCBVEBUsnMDxYyuJKtJvGcE5nKgvuPed3s8zXNI=
170172
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.6.0/go.mod h1:20QOZ3rBC9wTGgzXzLz9M6YheX0VaxWE0/JI+s8On7k=
171173
github.com/stackitcloud/stackit-sdk-go/services/logme v0.25.1 h1:hv5WrRU9rN6Jx4OwdOGJRyaQrfA9p1tzEoQK6/CDyoA=

stackit/internal/core/core.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ import (
1212
"github.com/hashicorp/terraform-plugin-log/tflog"
1313
)
1414

15-
// Separator used for concatenation of TF-internal resource ID
16-
const Separator = ","
17-
1815
type ResourceType string
1916

2017
const (
2118
Resource ResourceType = "resource"
2219
Datasource ResourceType = "datasource"
20+
21+
// Separator used for concatenation of TF-internal resource ID
22+
Separator = ","
23+
24+
ResourceRegionFallbackDocstring = "Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on resource level."
25+
DatasourceRegionFallbackDocstring = "Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on datasource level."
2326
)
2427

2528
type ProviderData struct {
@@ -33,6 +36,7 @@ type ProviderData struct {
3336
DnsCustomEndpoint string
3437
GitCustomEndpoint string
3538
IaaSCustomEndpoint string
39+
KMSCustomEndpoint string
3640
LoadBalancerCustomEndpoint string
3741
LogMeCustomEndpoint string
3842
MariaDBCustomEndpoint string
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package kms
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/hashicorp/terraform-plugin-framework/datasource"
9+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
10+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
11+
"github.com/hashicorp/terraform-plugin-log/tflog"
12+
"github.com/stackitcloud/stackit-sdk-go/services/kms"
13+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
14+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
15+
kmsUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/kms/utils"
16+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
17+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
18+
)
19+
20+
var (
21+
_ datasource.DataSource = &keyRingDataSource{}
22+
)
23+
24+
func NewKeyRingDataSource() datasource.DataSource {
25+
return &keyRingDataSource{}
26+
}
27+
28+
type keyRingDataSource struct {
29+
client *kms.APIClient
30+
providerData core.ProviderData
31+
}
32+
33+
func (k *keyRingDataSource) Metadata(_ context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) {
34+
response.TypeName = request.ProviderTypeName + "_kms_keyring"
35+
}
36+
37+
func (k *keyRingDataSource) Configure(ctx context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) {
38+
var ok bool
39+
k.providerData, ok = conversion.ParseProviderData(ctx, request.ProviderData, &response.Diagnostics)
40+
if !ok {
41+
return
42+
}
43+
44+
apiClient := kmsUtils.ConfigureClient(ctx, &k.providerData, &response.Diagnostics)
45+
if response.Diagnostics.HasError() {
46+
return
47+
}
48+
49+
k.client = apiClient
50+
tflog.Info(ctx, "KMS client configured")
51+
}
52+
53+
func (k *keyRingDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, response *datasource.SchemaResponse) {
54+
response.Schema = schema.Schema{
55+
Description: fmt.Sprintf("KMS Keyring datasource schema. %s", core.DatasourceRegionFallbackDocstring),
56+
Attributes: map[string]schema.Attribute{
57+
"description": schema.StringAttribute{
58+
Description: "A user chosen description to distinguish multiple keyrings.",
59+
Computed: true,
60+
},
61+
"display_name": schema.StringAttribute{
62+
Description: "The display name to distinguish multiple keyrings.",
63+
Computed: true,
64+
},
65+
"keyring_id": schema.StringAttribute{
66+
Description: "An auto generated unique id which identifies the keyring.",
67+
Required: true,
68+
Validators: []validator.String{
69+
validate.UUID(),
70+
validate.NoSeparator(),
71+
},
72+
},
73+
"id": schema.StringAttribute{
74+
Description: "Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`keyring_id`\".",
75+
Computed: true,
76+
},
77+
"project_id": schema.StringAttribute{
78+
Description: "STACKIT project ID to which the keyring is associated.",
79+
Required: true,
80+
Validators: []validator.String{
81+
validate.UUID(),
82+
validate.NoSeparator(),
83+
},
84+
},
85+
"region": schema.StringAttribute{
86+
Optional: true,
87+
// must be computed to allow for storing the override value from the provider
88+
Computed: true,
89+
Description: "The resource region. If not defined, the provider region is used.",
90+
},
91+
},
92+
}
93+
}
94+
95+
func (k *keyRingDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
96+
var model Model
97+
98+
diags := request.Config.Get(ctx, &model)
99+
response.Diagnostics.Append(diags...)
100+
if response.Diagnostics.HasError() {
101+
return
102+
}
103+
projectId := model.ProjectId.ValueString()
104+
keyRingId := model.KeyRingId.ValueString()
105+
region := k.providerData.GetRegionWithOverride(model.Region)
106+
107+
ctx = tflog.SetField(ctx, "keyring_id", keyRingId)
108+
ctx = tflog.SetField(ctx, "project_id", projectId)
109+
ctx = tflog.SetField(ctx, "region", region)
110+
111+
keyRingResponse, err := k.client.GetKeyRing(ctx, projectId, region, keyRingId).Execute()
112+
if err != nil {
113+
utils.LogError(
114+
ctx,
115+
&response.Diagnostics,
116+
err,
117+
"Reading keyring",
118+
fmt.Sprintf("Keyring with ID %q does not exist in project %q.", keyRingId, projectId),
119+
map[int]string{
120+
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
121+
},
122+
)
123+
response.State.RemoveResource(ctx)
124+
return
125+
}
126+
127+
err = mapFields(keyRingResponse, &model, region)
128+
if err != nil {
129+
core.LogAndAddError(ctx, &response.Diagnostics, "Error reading keyring", fmt.Sprintf("Processing API payload: %v", err))
130+
return
131+
}
132+
133+
diags = response.State.Set(ctx, &model)
134+
response.Diagnostics.Append(diags...)
135+
if response.Diagnostics.HasError() {
136+
return
137+
}
138+
tflog.Info(ctx, "Key ring read")
139+
}

0 commit comments

Comments
 (0)