Skip to content

Commit 3f18c2c

Browse files
committed
better update
1 parent c42b1a6 commit 3f18c2c

File tree

6 files changed

+539
-551
lines changed

6 files changed

+539
-551
lines changed

internal/locality/validation.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,25 @@ func ValidateStringInSliceWithWarning(correctValues []string, field string) sche
3030
}
3131
}
3232

33-
func ValidateRegionalUUID() schema.SchemaValidateFunc {
33+
func ValidateRegionalUUID() schema.SchemaValidateDiagFunc {
3434
regions := make([]string, 0, len(scw.AllRegions))
3535
for _, region := range scw.AllRegions {
3636
regions = append(regions, region.String())
3737
}
3838

39-
return validation.StringMatch(regexp.MustCompile(`^`+strings.Join(regions, "|")+`/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`), "must be in the form region/UUID")
39+
return func(i interface{}, path cty.Path) diag.Diagnostics {
40+
var res diag.Diagnostics
41+
42+
_, rawErr := validation.StringMatch(regexp.MustCompile(`^`+strings.Join(regions, "|")+`/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`), "must be in the form region/UUID")(i, "")
43+
44+
for _, e := range rawErr {
45+
res = append(res, diag.Diagnostic{
46+
Severity: diag.Error,
47+
Summary: e.Error(),
48+
AttributePath: path,
49+
})
50+
}
51+
52+
return res
53+
}
4054
}

internal/services/jobs/definition.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,13 @@ func ResourceDefinition() *schema.Resource {
9797
Type: schema.TypeSet,
9898
Optional: true,
9999
Description: "A reference to a Secret Manager secret.",
100-
//Set: func(v interface{}) int {
101-
// secret := v.(map[string]interface{})
102-
// if secret["file"] == nil {
103-
// return schema.HashString(secret["secret_id"].(string) + secret["secret_version"].(string) + secret["environment"].(string))
104-
// }
105-
// return schema.HashString(secret["secret_id"].(string) + secret["secret_version"].(string) + secret["file"].(string))
106-
//},
107100
Elem: &schema.Resource{
108101
Schema: map[string]*schema.Schema{
109102
"secret_id": {
110-
Type: schema.TypeString,
111-
Description: "The secret unique identifier, it must be in the form region/UUID. The region must be the same as the job definition.",
112-
Required: true,
113-
ValidateFunc: locality.ValidateRegionalUUID(),
103+
Type: schema.TypeString,
104+
Description: "The secret unique identifier, it must be in the form region/UUID. The region must be the same as the job definition.",
105+
Required: true,
106+
ValidateDiagFunc: locality.ValidateRegionalUUID(),
114107
},
115108
"secret_reference_id": {
116109
Type: schema.TypeString,
@@ -146,6 +139,7 @@ func ResourceJobDefinitionCreate(ctx context.Context, d *schema.ResourceData, m
146139
if err != nil {
147140
return diag.FromErr(err)
148141
}
142+
149143
req := &jobs.CreateJobDefinitionRequest{
150144
Region: region,
151145
Name: types.ExpandOrGenerateString(d.Get("name").(string), "job"),
@@ -179,7 +173,7 @@ func ResourceJobDefinitionCreate(ctx context.Context, d *schema.ResourceData, m
179173
}
180174

181175
if rawSecretReference, ok := d.GetOk("secret_reference"); ok {
182-
if err := CreateJobDefinitionSecret(expandJobDefinitionSecret(rawSecretReference), api, region, definition.ID); err != nil {
176+
if err := CreateJobDefinitionSecret(ctx, api, expandJobDefinitionSecret(rawSecretReference), region, definition.ID); err != nil {
183177
return diag.FromErr(err)
184178
}
185179
}
@@ -222,7 +216,7 @@ func ResourceJobDefinitionRead(ctx context.Context, d *schema.ResourceData, m in
222216
for i, secret := range rawSecretRefs.Secrets {
223217
secretRef := make(map[string]interface{})
224218
secretRef["secret_id"] = fmt.Sprintf("%s/%s", definition.Region, secret.SecretManagerID)
225-
//secretRef["secret_id"] = secret.SecretManagerID
219+
// secretRef["secret_id"] = secret.SecretManagerID
226220
secretRef["secret_reference_id"] = secret.SecretID
227221
secretRef["secret_version"] = secret.SecretManagerVersion
228222

@@ -313,21 +307,29 @@ func ResourceJobDefinitionUpdate(ctx context.Context, d *schema.ResourceData, m
313307
}
314308

315309
if d.HasChange("secret_reference") {
316-
oldRawSecretRefs, _ := d.GetChange("secret_reference")
310+
oldRawSecretRefs, newRawSecretRefs := d.GetChange("secret_reference")
311+
317312
oldSecretRefs := expandJobDefinitionSecret(oldRawSecretRefs)
313+
newSecretRefs := expandJobDefinitionSecret(newRawSecretRefs)
318314

319-
for _, oldSecretRef := range oldSecretRefs {
320-
if err := api.DeleteJobDefinitionSecret(&jobs.DeleteJobDefinitionSecretRequest{
315+
toCreate, toDelete, err := DiffJobDefinitionSecrets(oldSecretRefs, newSecretRefs)
316+
if err != nil {
317+
return diag.FromErr(err)
318+
}
319+
320+
for _, secret := range toDelete {
321+
deleteReq := &jobs.DeleteJobDefinitionSecretRequest{
321322
Region: region,
322323
JobDefinitionID: id,
323-
SecretID: oldSecretRef.SecretReferenceID,
324-
}); err != nil {
324+
SecretID: secret.SecretReferenceID,
325+
}
326+
if err := api.DeleteJobDefinitionSecret(deleteReq, scw.WithContext(ctx)); err != nil {
325327
return diag.FromErr(err)
326328
}
327329
}
328330

329-
if rawSecretReference, ok := d.GetOk("secret_reference"); ok {
330-
if err := CreateJobDefinitionSecret(expandJobDefinitionSecret(rawSecretReference), api, region, id); err != nil {
331+
if len(toCreate) > 0 {
332+
if err := CreateJobDefinitionSecret(ctx, api, toCreate, region, id); err != nil {
331333
return diag.FromErr(err)
332334
}
333335
}

internal/services/jobs/definition_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func TestAccJobDefinition_SecretReference(t *testing.T) {
227227
secret_reference {
228228
secret_id = scaleway_secret.main.id
229229
secret_version = "latest"
230-
file = "/home/dev/new_env"
230+
file = "/home/dev/secret_file"
231231
}
232232
secret_reference {
233233
secret_id = scaleway_secret.main.id
@@ -241,7 +241,7 @@ func TestAccJobDefinition_SecretReference(t *testing.T) {
241241
acctest.CheckResourceAttrUUID("scaleway_job_definition.main", "id"),
242242
resource.TestCheckResourceAttr("scaleway_job_definition.main", "name", "test-jobs-job-definition-secret"),
243243
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.#", "2"),
244-
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.0.file", "/home/dev/new_env"),
244+
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.0.file", "/home/dev/secret_file"),
245245
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.1.environment", "SOME_ENV"),
246246
),
247247
},
@@ -264,7 +264,7 @@ func TestAccJobDefinition_SecretReference(t *testing.T) {
264264
secret_reference {
265265
secret_id = scaleway_secret.main.id
266266
secret_version = "latest"
267-
file = "/home/dev/new_env"
267+
file = "/home/dev/secret_file"
268268
}
269269
}
270270
`,
@@ -273,7 +273,7 @@ func TestAccJobDefinition_SecretReference(t *testing.T) {
273273
acctest.CheckResourceAttrUUID("scaleway_job_definition.main", "id"),
274274
resource.TestCheckResourceAttr("scaleway_job_definition.main", "name", "test-jobs-job-definition-secret"),
275275
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.#", "1"),
276-
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.0.file", "/home/dev/new_env"),
276+
resource.TestCheckResourceAttr("scaleway_job_definition.main", "secret_reference.0.file", "/home/dev/secret_file"),
277277
),
278278
},
279279
},
@@ -413,6 +413,6 @@ func TestCreateJobDefinitionSecret(t *testing.T) {
413413
region := scw.RegionFrPar
414414
jobID := "22222222-2222-2222-2222-222222222222"
415415

416-
err := jobs.CreateJobDefinitionSecret(jobSecrets, api, region, jobID)
416+
err := jobs.CreateJobDefinitionSecret(t.Context(), api, jobSecrets, region, jobID)
417417
assert.ErrorContains(t, err, fmt.Sprintf("the secret id %s does not appear to be in the same region as the job definition id %s", jobSecrets[1].SecretID, jobID))
418418
}

internal/services/jobs/helpers.go

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package jobs
22

33
import (
4+
"bytes"
5+
"context"
46
"fmt"
57

68
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
79
jobs "github.com/scaleway/scaleway-sdk-go/api/jobs/v1alpha1"
810
"github.com/scaleway/scaleway-sdk-go/scw"
911
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1012
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
1114
)
1215

1316
// newAPIWithRegion returns a new jobs API and the region for a Create request
@@ -134,8 +137,7 @@ func expandJobDefinitionSecret(i any) []JobDefinitionSecret {
134137
return parsedSecrets
135138
}
136139

137-
func CreateJobDefinitionSecret(jobSecrets []JobDefinitionSecret, api *jobs.API, region scw.Region, jobID string) error {
138-
140+
func CreateJobDefinitionSecret(ctx context.Context, api *jobs.API, jobSecrets []JobDefinitionSecret, region scw.Region, jobID string) error {
139141
secretConfigs := []*jobs.CreateJobDefinitionSecretsRequestSecretConfig{}
140142

141143
for _, parsedSecretRef := range jobSecrets {
@@ -155,28 +157,92 @@ func CreateJobDefinitionSecret(jobSecrets []JobDefinitionSecret, api *jobs.API,
155157
secretConfig.SecretManagerID = secretID
156158
secretConfig.SecretManagerVersion = parsedSecretRef.SecretVersion
157159

160+
if err := validateJobDefinitionSecret(&parsedSecretRef); err != nil {
161+
return err
162+
}
163+
158164
if parsedSecretRef.Environment != "" {
159165
secretConfig.EnvVarName = &parsedSecretRef.Environment
160166
}
161167

162168
if parsedSecretRef.File != "" {
163169
secretConfig.Path = &parsedSecretRef.File
164170
}
165-
166-
if parsedSecretRef.Environment != "" && parsedSecretRef.File != "" {
167-
return fmt.Errorf("the secret id %s must have exactly one mount point: file or environment", parsedSecretRef.SecretID)
168-
}
169-
170-
if parsedSecretRef.Environment == "" && parsedSecretRef.File == "" {
171-
return fmt.Errorf("the secret id %s is missing a mount point: file or environment", parsedSecretRef.SecretID)
172-
}
173171
}
174172

175173
_, err := api.CreateJobDefinitionSecrets(&jobs.CreateJobDefinitionSecretsRequest{
176174
Region: region,
177175
JobDefinitionID: jobID,
178176
Secrets: secretConfigs,
179-
})
177+
}, scw.WithContext(ctx))
180178

181179
return err
182180
}
181+
182+
func crc32Hash(secret *JobDefinitionSecret) int {
183+
buf := bytes.NewBufferString(secret.SecretID)
184+
buf.WriteString(secret.SecretVersion)
185+
186+
if secret.Environment != "" {
187+
buf.WriteString(secret.Environment)
188+
}
189+
190+
if secret.File != "" {
191+
buf.WriteString(secret.File)
192+
}
193+
194+
return types.StringHashcode(buf.String())
195+
}
196+
197+
func DiffJobDefinitionSecrets(oldSecretRefs, newSecretRefs []JobDefinitionSecret) (toCreate []JobDefinitionSecret, toDelete []JobDefinitionSecret, err error) {
198+
toCreate = make([]JobDefinitionSecret, 0)
199+
toDelete = make([]JobDefinitionSecret, 0)
200+
201+
// hash the new and old secret sets
202+
oldSecretRefsMap := make(map[int]JobDefinitionSecret, len(oldSecretRefs))
203+
for _, secret := range oldSecretRefs {
204+
oldSecretRefsMap[crc32Hash(&secret)] = secret
205+
}
206+
207+
newSecretRefsMap := make(map[int]JobDefinitionSecret, len(newSecretRefs))
208+
209+
for _, secret := range newSecretRefs {
210+
if err := validateJobDefinitionSecret(&secret); err != nil {
211+
return toCreate, toDelete, err
212+
}
213+
214+
newSecretRefsMap[crc32Hash(&secret)] = secret
215+
}
216+
217+
// find secrets to delete
218+
for hash, secret := range oldSecretRefsMap {
219+
if _, ok := newSecretRefsMap[hash]; !ok {
220+
toDelete = append(toDelete, secret)
221+
}
222+
}
223+
224+
// find secrets to create
225+
for hash, secret := range newSecretRefsMap {
226+
if _, ok := oldSecretRefsMap[hash]; !ok {
227+
toCreate = append(toCreate, secret)
228+
}
229+
}
230+
231+
return toCreate, toDelete, nil
232+
}
233+
234+
func validateJobDefinitionSecret(secret *JobDefinitionSecret) error {
235+
if secret == nil {
236+
return nil
237+
}
238+
239+
if secret.Environment != "" && secret.File != "" {
240+
return fmt.Errorf("the secret id %s must have exactly one mount point: file or environment", secret.SecretID)
241+
}
242+
243+
if secret.Environment == "" && secret.File == "" {
244+
return fmt.Errorf("the secret id %s is missing a mount point: file or environment", secret.SecretID)
245+
}
246+
247+
return nil
248+
}

0 commit comments

Comments
 (0)