Skip to content

Commit 02165fa

Browse files
committed
feat(key_manager); add support for key manager key
1 parent 16eb0eb commit 02165fa

File tree

4 files changed

+339
-0
lines changed

4 files changed

+339
-0
lines changed

internal/provider/provider.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"os"
66

7+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/keymanager"
8+
79
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
810
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
911
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
@@ -195,6 +197,7 @@ func Provider(config *Config) plugin.ProviderFunc {
195197
"scaleway_k8s_acl": k8s.ResourceACL(),
196198
"scaleway_k8s_cluster": k8s.ResourceCluster(),
197199
"scaleway_k8s_pool": k8s.ResourcePool(),
200+
"scaleway_key_manager_key": keymanager.ResourceKey(),
198201
"scaleway_lb": lb.ResourceLb(),
199202
"scaleway_lb_acl": lb.ResourceACL(),
200203
"scaleway_lb_backend": lb.ResourceBackend(),
@@ -297,6 +300,7 @@ func Provider(config *Config) plugin.ProviderFunc {
297300
"scaleway_k8s_cluster": k8s.DataSourceCluster(),
298301
"scaleway_k8s_pool": k8s.DataSourcePool(),
299302
"scaleway_k8s_version": k8s.DataSourceVersion(),
303+
"scaleway_key_manager_key": keymanager.DataSourceKey(),
300304
"scaleway_lb": lb.DataSourceLb(),
301305
"scaleway_lb_acls": lb.DataSourceACLs(),
302306
"scaleway_lb_backend": lb.DataSourceBackend(),
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package keymanager
2+
3+
import (
4+
"time"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
7+
key_manager "github.com/scaleway/scaleway-sdk-go/api/key_manager/v1alpha1"
8+
"github.com/scaleway/scaleway-sdk-go/scw"
9+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
10+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
11+
)
12+
13+
const defaultKeyTimeout = 5 * time.Minute
14+
15+
func newKeyManagerAPI(d *schema.ResourceData, m any) (*key_manager.API, scw.Region, error) {
16+
api := key_manager.NewAPI(meta.ExtractScwClient(m))
17+
18+
region, err := meta.ExtractRegion(d, m)
19+
if err != nil {
20+
return nil, "", err
21+
}
22+
23+
return api, region, nil
24+
}
25+
26+
func NewKeyManagerAPIWithRegionAndID(m any, regionalID string) (*key_manager.API, scw.Region, string, error) {
27+
api := key_manager.NewAPI(meta.ExtractScwClient(m))
28+
29+
region, ID, err := regional.ParseID(regionalID)
30+
if err != nil {
31+
return nil, "", "", err
32+
}
33+
34+
return api, region, ID, nil
35+
}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package keymanager
2+
3+
import (
4+
"context"
5+
6+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
7+
8+
key_manager "github.com/scaleway/scaleway-sdk-go/api/key_manager/v1alpha1"
9+
"github.com/scaleway/scaleway-sdk-go/scw"
10+
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
15+
)
16+
17+
func ResourceKey() *schema.Resource {
18+
return &schema.Resource{
19+
CreateContext: resourceKeyCreate,
20+
ReadContext: resourceKeyRead,
21+
UpdateContext: resourceKeyUpdate,
22+
DeleteContext: resourceKeyDelete,
23+
Importer: &schema.ResourceImporter{
24+
StateContext: schema.ImportStatePassthroughContext,
25+
},
26+
Timeouts: &schema.ResourceTimeout{
27+
Read: schema.DefaultTimeout(defaultKeyTimeout),
28+
Update: schema.DefaultTimeout(defaultKeyTimeout),
29+
Delete: schema.DefaultTimeout(defaultKeyTimeout),
30+
Default: schema.DefaultTimeout(defaultKeyTimeout),
31+
},
32+
SchemaVersion: 0,
33+
Schema: map[string]*schema.Schema{
34+
"name": {
35+
Type: schema.TypeString,
36+
Optional: true,
37+
Description: "The name of the key.",
38+
},
39+
"description": {
40+
Type: schema.TypeString,
41+
Optional: true,
42+
Description: "The description of the key.",
43+
},
44+
"state": {
45+
Type: schema.TypeString,
46+
Computed: true,
47+
Description: "The state of the key (enabled, disabled, pending_key_material)",
48+
},
49+
"locked": {
50+
Type: schema.TypeBool,
51+
Computed: true,
52+
Description: "The locked state of the key.",
53+
},
54+
"protected": {
55+
Type: schema.TypeBool,
56+
Computed: true,
57+
Description: "Returns `true` if key protection is applied to the key",
58+
},
59+
"rotation_count": {
60+
Type: schema.TypeInt,
61+
Computed: true,
62+
Description: "The number of times the key has been rotated",
63+
},
64+
"rotated_at": {
65+
Type: schema.TypeString,
66+
Computed: true,
67+
Description: "The date and time of the last rotation of the key",
68+
},
69+
"created_at": {
70+
Type: schema.TypeString,
71+
Computed: true,
72+
Description: "The date and time of the creation of the key",
73+
},
74+
"updated_at": {
75+
Type: schema.TypeString,
76+
Computed: true,
77+
Description: "The date and time of the last update of the key",
78+
},
79+
"tags": {
80+
Type: schema.TypeList,
81+
Optional: true,
82+
Elem: &schema.Schema{
83+
Type: schema.TypeString,
84+
},
85+
Description: "List of tags [\"tag1\", \"tag2\", ...] attached to a key",
86+
},
87+
"origin": {
88+
Type: schema.TypeString,
89+
Computed: true,
90+
Description: "The origin of the key (scaleway_kms, external)",
91+
},
92+
// Computed
93+
"project_id": account.ProjectIDSchema(),
94+
"region": regional.ComputedSchema(),
95+
},
96+
}
97+
}
98+
99+
func resourceKeyCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
100+
api, region, err := newKeyManagerAPI(d, meta)
101+
if err != nil {
102+
return diag.FromErr(err)
103+
}
104+
105+
keyCreateRequest := &key_manager.CreateKeyRequest{
106+
Region: region,
107+
ProjectID: d.Get("project_id").(string),
108+
Name: types.ExpandStringPtr(d.Get("name")),
109+
Description: types.ExpandStringPtr(d.Get("description")),
110+
Unprotected: d.Get("protected").(bool),
111+
}
112+
113+
rawTag, tagExist := d.GetOk("tags")
114+
if tagExist {
115+
keyCreateRequest.Tags = types.ExpandStrings(rawTag)
116+
}
117+
118+
rawDescription, descriptionExist := d.GetOk("description")
119+
if descriptionExist {
120+
keyCreateRequest.Description = types.ExpandStringPtr(rawDescription)
121+
}
122+
123+
keyResponse, err := api.CreateKey(keyCreateRequest, scw.WithContext(ctx))
124+
if err != nil {
125+
return diag.FromErr(err)
126+
}
127+
128+
d.SetId(regional.NewIDString(region, keyResponse.ID))
129+
130+
return resourceKeyRead(ctx, d, meta)
131+
}
132+
133+
func resourceKeyRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
134+
api, region, id, err := NewKeyManagerAPIWithRegionAndID(meta, d.Id())
135+
if err != nil {
136+
return diag.FromErr(err)
137+
}
138+
139+
key, err := api.GetKey(&key_manager.GetKeyRequest{
140+
Region: region,
141+
KeyID: id,
142+
}, scw.WithContext(ctx))
143+
if err != nil {
144+
return diag.FromErr(err)
145+
}
146+
147+
_ = d.Set("region", key.Region)
148+
_ = d.Set("project_id", key.ProjectID)
149+
_ = d.Set("description", key.Description)
150+
_ = d.Set("name", key.Name)
151+
_ = d.Set("state", key.State)
152+
_ = d.Set("rotation_count", key.RotationCount)
153+
_ = d.Set("created_at", types.FlattenTime(key.CreatedAt))
154+
_ = d.Set("updated_at", types.FlattenTime(key.UpdatedAt))
155+
_ = d.Set("origin", key.Origin)
156+
_ = d.Set("rotated_at", types.FlattenTime(key.RotatedAt))
157+
_ = d.Set("locked", key.Locked)
158+
_ = d.Set("protected", key.Protected)
159+
160+
return nil
161+
}
162+
163+
func resourceKeyUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
164+
keyManagerAPI, region, ID, err := NewKeyManagerAPIWithRegionAndID(meta, d.Id())
165+
if err != nil {
166+
return diag.FromErr(err)
167+
}
168+
169+
shouldUpdate := false
170+
req := &key_manager.UpdateKeyRequest{
171+
Region: region,
172+
KeyID: ID,
173+
}
174+
175+
if d.HasChange("name") {
176+
req.Name = types.ExpandStringPtr(d.Get("name"))
177+
shouldUpdate = true
178+
}
179+
180+
if d.HasChange("tags") {
181+
req.Tags = types.ExpandUpdatedStringsPtr(d.Get("tags"))
182+
}
183+
184+
if shouldUpdate {
185+
_, err = keyManagerAPI.UpdateKey(req, scw.WithContext(ctx))
186+
if err != nil {
187+
return diag.FromErr(err)
188+
}
189+
}
190+
191+
return resourceKeyRead(ctx, d, meta)
192+
}
193+
194+
func resourceKeyDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
195+
keyManagerAPI, region, ID, err := NewKeyManagerAPIWithRegionAndID(meta, d.Id())
196+
if err != nil {
197+
return diag.FromErr(err)
198+
}
199+
200+
err = keyManagerAPI.DeleteKey(&key_manager.DeleteKeyRequest{
201+
KeyID: ID,
202+
Region: region,
203+
}, scw.WithContext(ctx))
204+
if err != nil {
205+
return diag.FromErr(err)
206+
}
207+
208+
return nil
209+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package keymanager
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
key_manager "github.com/scaleway/scaleway-sdk-go/api/key_manager/v1alpha1"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/datasource"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
16+
)
17+
18+
func DataSourceKey() *schema.Resource {
19+
// Generate datasource schema from resource
20+
dsSchema := datasource.SchemaFromResourceSchema(ResourceKey().Schema)
21+
// Set 'Optional' schema elements
22+
datasource.AddOptionalFieldsToSchema(dsSchema, "name", "project_id")
23+
24+
dsSchema["name"].ConflictsWith = []string{"key_id"}
25+
dsSchema["key_id"] = &schema.Schema{
26+
Type: schema.TypeString,
27+
Optional: true,
28+
Description: "The ID of the Key",
29+
ConflictsWith: []string{"name"},
30+
ValidateDiagFunc: verify.IsUUIDorUUIDWithLocality(),
31+
}
32+
33+
return &schema.Resource{
34+
ReadContext: DataSourceKeyRead,
35+
Schema: dsSchema,
36+
}
37+
}
38+
39+
func DataSourceKeyRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
40+
api, region, err := newKeyManagerAPI(d, m)
41+
if err != nil {
42+
return diag.FromErr(err)
43+
}
44+
45+
keyID, ok := d.GetOk("key_id")
46+
if !ok {
47+
keyName := d.Get("name").(string)
48+
49+
res, err := api.ListKeys(&key_manager.ListKeysRequest{
50+
Region: region,
51+
Name: types.ExpandStringPtr(keyName),
52+
ProjectID: types.ExpandStringPtr(d.Get("project_id")),
53+
}, scw.WithContext(ctx))
54+
if err != nil {
55+
return diag.FromErr(err)
56+
}
57+
58+
foundKey, err := datasource.FindExact(
59+
res.Keys,
60+
func(s *key_manager.Key) bool { return s.Name == keyName },
61+
keyName,
62+
)
63+
if err != nil {
64+
return diag.FromErr(err)
65+
}
66+
67+
keyID = foundKey.ID
68+
}
69+
70+
regionalID := datasource.NewRegionalID(keyID, region)
71+
d.SetId(regionalID)
72+
73+
err = d.Set("key_id", regionalID)
74+
if err != nil {
75+
return diag.FromErr(err)
76+
}
77+
78+
// Check if key exist as Read will return nil if resource does not exist
79+
// keyID may be regional if using name in data source
80+
getReq := &key_manager.GetKeyRequest{
81+
Region: region,
82+
KeyID: locality.ExpandID(keyID.(string)),
83+
}
84+
85+
_, err = api.GetKey(getReq, scw.WithContext(ctx))
86+
if err != nil {
87+
return diag.FromErr(fmt.Errorf("no key found with the id %s", keyID))
88+
}
89+
90+
return resourceKeyRead(ctx, d, m)
91+
}

0 commit comments

Comments
 (0)