Skip to content

Commit 41ab905

Browse files
Add gc_rules to BigTable's Garbage Collection policy TF resource. (#5800) (#4212)
Signed-off-by: Modular Magician <[email protected]>
1 parent 5c43687 commit 41ab905

File tree

4 files changed

+408
-9
lines changed

4 files changed

+408
-9
lines changed

.changelog/5800.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
bigtable: added `gc_rules` to `google_bigtable_gc_policy` resource.
3+
```

google-beta/resource_bigtable_gc_policy.go

Lines changed: 130 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package google
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"log"
8+
"strings"
79
"time"
810

911
"cloud.google.com/go/bigtable"
1012
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
1114
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1215
)
1316

@@ -56,9 +59,10 @@ func resourceBigtableGCPolicyCustomizeDiff(_ context.Context, d *schema.Resource
5659

5760
func resourceBigtableGCPolicy() *schema.Resource {
5861
return &schema.Resource{
59-
Create: resourceBigtableGCPolicyCreate,
62+
Create: resourceBigtableGCPolicyUpsert,
6063
Read: resourceBigtableGCPolicyRead,
6164
Delete: resourceBigtableGCPolicyDestroy,
65+
Update: resourceBigtableGCPolicyUpsert,
6266
CustomizeDiff: resourceBigtableGCPolicyCustomizeDiff,
6367

6468
Schema: map[string]*schema.Schema{
@@ -84,12 +88,24 @@ func resourceBigtableGCPolicy() *schema.Resource {
8488
Description: `The name of the column family.`,
8589
},
8690

91+
"gc_rules": {
92+
Type: schema.TypeString,
93+
Optional: true,
94+
Description: `Serialized JSON string for garbage collection policy. Conflicts with "mode", "max_age" and "max_version".`,
95+
ValidateFunc: validation.StringIsJSON,
96+
ConflictsWith: []string{"mode", "max_age", "max_version"},
97+
StateFunc: func(v interface{}) string {
98+
json, _ := structure.NormalizeJsonString(v)
99+
return json
100+
},
101+
},
87102
"mode": {
88-
Type: schema.TypeString,
89-
Optional: true,
90-
ForceNew: true,
91-
Description: `If multiple policies are set, you should choose between UNION OR INTERSECTION.`,
92-
ValidateFunc: validation.StringInSlice([]string{GCPolicyModeIntersection, GCPolicyModeUnion}, false),
103+
Type: schema.TypeString,
104+
Optional: true,
105+
ForceNew: true,
106+
Description: `If multiple policies are set, you should choose between UNION OR INTERSECTION.`,
107+
ValidateFunc: validation.StringInSlice([]string{GCPolicyModeIntersection, GCPolicyModeUnion}, false),
108+
ConflictsWith: []string{"gc_rules"},
93109
},
94110

95111
"max_age": {
@@ -120,6 +136,7 @@ func resourceBigtableGCPolicy() *schema.Resource {
120136
},
121137
},
122138
},
139+
ConflictsWith: []string{"gc_rules"},
123140
},
124141

125142
"max_version": {
@@ -137,6 +154,7 @@ func resourceBigtableGCPolicy() *schema.Resource {
137154
},
138155
},
139156
},
157+
ConflictsWith: []string{"gc_rules"},
140158
},
141159

142160
"project": {
@@ -151,7 +169,7 @@ func resourceBigtableGCPolicy() *schema.Resource {
151169
}
152170
}
153171

154-
func resourceBigtableGCPolicyCreate(d *schema.ResourceData, meta interface{}) error {
172+
func resourceBigtableGCPolicyUpsert(d *schema.ResourceData, meta interface{}) error {
155173
config := meta.(*Config)
156174
userAgent, err := generateUserAgentString(d, config.userAgent)
157175
if err != nil {
@@ -288,13 +306,22 @@ func generateBigtableGCPolicy(d *schema.ResourceData) (bigtable.GCPolicy, error)
288306
mode := d.Get("mode").(string)
289307
ma, aok := d.GetOk("max_age")
290308
mv, vok := d.GetOk("max_version")
309+
gcRules, gok := d.GetOk("gc_rules")
291310

292-
if !aok && !vok {
311+
if !aok && !vok && !gok {
293312
return bigtable.NoGcPolicy(), nil
294313
}
295314

296315
if mode == "" && aok && vok {
297-
return nil, fmt.Errorf("If multiple policies are set, mode can't be empty")
316+
return nil, fmt.Errorf("if multiple policies are set, mode can't be empty")
317+
}
318+
319+
if gok {
320+
var j map[string]interface{}
321+
if err := json.Unmarshal([]byte(gcRules.(string)), &j); err != nil {
322+
return nil, err
323+
}
324+
return getGCPolicyFromJSON(j)
298325
}
299326

300327
if aok {
@@ -324,6 +351,100 @@ func generateBigtableGCPolicy(d *schema.ResourceData) (bigtable.GCPolicy, error)
324351
return policies[0], nil
325352
}
326353

354+
func getGCPolicyFromJSON(topLevelPolicy map[string]interface{}) (bigtable.GCPolicy, error) {
355+
policy := []bigtable.GCPolicy{}
356+
357+
if err := validateNestedPolicy(topLevelPolicy, true); err != nil {
358+
return nil, err
359+
}
360+
361+
for _, p := range topLevelPolicy["rules"].([]interface{}) {
362+
childPolicy := p.(map[string]interface{})
363+
if err := validateNestedPolicy(childPolicy, false); err != nil {
364+
return nil, err
365+
}
366+
367+
if childPolicy["max_age"] != nil {
368+
maxAge := childPolicy["max_age"].(string)
369+
duration, err := time.ParseDuration(maxAge)
370+
if err != nil {
371+
return nil, fmt.Errorf("invalid duration string: %v", maxAge)
372+
}
373+
policy = append(policy, bigtable.MaxAgePolicy(duration))
374+
}
375+
376+
if childPolicy["max_version"] != nil {
377+
version := childPolicy["max_version"].(float64)
378+
policy = append(policy, bigtable.MaxVersionsPolicy(int(version)))
379+
}
380+
381+
if childPolicy["mode"] != nil {
382+
n, err := getGCPolicyFromJSON(childPolicy)
383+
if err != nil {
384+
return nil, err
385+
}
386+
policy = append(policy, n)
387+
}
388+
}
389+
390+
switch topLevelPolicy["mode"] {
391+
case strings.ToLower(GCPolicyModeUnion):
392+
return bigtable.UnionPolicy(policy...), nil
393+
case strings.ToLower(GCPolicyModeIntersection):
394+
return bigtable.IntersectionPolicy(policy...), nil
395+
default:
396+
return policy[0], nil
397+
}
398+
}
399+
400+
func validateNestedPolicy(p map[string]interface{}, topLevel bool) error {
401+
if len(p) > 2 {
402+
return fmt.Errorf("rules has more than 2 fields")
403+
}
404+
maxVersion, maxVersionOk := p["max_version"]
405+
maxAge, maxAgeOk := p["max_age"]
406+
rulesObj, rulesOk := p["rules"]
407+
408+
_, modeOk := p["mode"]
409+
rules, arrOk := rulesObj.([]interface{})
410+
_, vCastOk := maxVersion.(float64)
411+
_, aCastOk := maxAge.(string)
412+
413+
if rulesOk && !arrOk {
414+
return fmt.Errorf("`rules` must be array")
415+
}
416+
417+
if modeOk && len(rules) < 2 {
418+
return fmt.Errorf("`rules` need at least 2 GC rule when mode is specified")
419+
}
420+
421+
if topLevel && !rulesOk {
422+
return fmt.Errorf("invalid nested policy, need `rules`")
423+
}
424+
425+
if topLevel && !modeOk && len(rules) != 1 {
426+
return fmt.Errorf("when `mode` is not specified, `rules` can only have 1 child rule")
427+
}
428+
429+
if !topLevel && len(p) == 2 && (!modeOk || !rulesOk) {
430+
return fmt.Errorf("need `mode` and `rules` for child nested policies")
431+
}
432+
433+
if !topLevel && len(p) == 1 && !maxVersionOk && !maxAgeOk {
434+
return fmt.Errorf("need `max_version` or `max_age` for the rule")
435+
}
436+
437+
if maxVersionOk && !vCastOk {
438+
return fmt.Errorf("`max_version` must be a number")
439+
}
440+
441+
if maxAgeOk && !aCastOk {
442+
return fmt.Errorf("`max_age must be a string")
443+
}
444+
445+
return nil
446+
}
447+
327448
func getMaxAgeDuration(values map[string]interface{}) (time.Duration, error) {
328449
d := values["duration"].(string)
329450
if d != "" {

0 commit comments

Comments
 (0)