Skip to content

Commit 2653605

Browse files
authored
Add support for DB Adv TTL Mgmt (hashicorp#2011)
* add support for schedule-based roles
1 parent 8adb474 commit 2653605

5 files changed

+261
-108
lines changed

docker-compose.yaml

+9-6
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ services:
1616
VAULT_TOKEN: "TEST"
1717
MYSQL_URL: "vault:vault@tcp(mysql:3306)/"
1818

19+
# to run this container on Mac M1 you need to
20+
# export DOCKER_DEFAULT_PLATFORM=linux/amd64
21+
# to run acceptance tests locally you need to
22+
# export MYSQL_URL=root:mysql@tcp(localhost:3306)/
23+
# to run the docker container
24+
# docker compose up -d mysql
1925
mysql:
20-
image: mysql:5.7
26+
image: docker.mirror.hashicorp.services/mysql:latest
2127
command: --default-authentication-plugin=mysql_native_password
2228
ports:
2329
- "3306:3306"
2430
environment:
25-
MYSQL_ROOT_PASSWORD: "root"
26-
MYSQL_DATABASE: "main"
27-
MYSQL_USER: "vault"
28-
MYSQL_PASSWORD: "vault"
31+
MYSQL_ROOT_PASSWORD: "mysql"
2932

3033
elastic:
3134
image: elasticsearch:7.17.10
@@ -95,4 +98,4 @@ services:
9598
ports:
9699
- "9042:9042"
97100
volumes:
98-
- ./testdata/cassandra.yaml:/etc/cassandra/cassandra.yaml
101+
- ./testdata/cassandra.yaml:/etc/cassandra/cassandra.yaml

internal/consts/consts.go

+3
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ const (
352352
FieldCredentialType = "credential_type"
353353
FieldFilename = "filename"
354354
FieldDefault = "default"
355+
FieldRotationStatements = "rotation_statements"
356+
FieldRotationSchedule = "rotation_schedule"
357+
FieldRotationWindow = "rotation_window"
355358
FieldKubernetesCACert = "kubernetes_ca_cert"
356359
FieldDisableLocalCAJWT = "disable_local_ca_jwt"
357360
FieldKubernetesHost = "kubernetes_host"

vault/resource_database_secret_backend_static_role.go

+87-68
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
package vault
55

66
import (
7-
"encoding/json"
7+
"context"
88
"fmt"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10+
"github.com/hashicorp/terraform-provider-vault/internal/consts"
911
"log"
1012
"regexp"
1113
"strings"
@@ -20,39 +22,44 @@ var (
2022
databaseSecretBackendStaticRoleNameFromPathRegex = regexp.MustCompile("^.+/static-roles/(.+$)")
2123
)
2224

25+
var staticRoleFields = []string{
26+
consts.FieldRotationPeriod,
27+
consts.FieldRotationStatements,
28+
consts.FieldDBName,
29+
}
30+
2331
func databaseSecretBackendStaticRoleResource() *schema.Resource {
2432
return &schema.Resource{
25-
Create: databaseSecretBackendStaticRoleWrite,
26-
Read: provider.ReadWrapper(databaseSecretBackendStaticRoleRead),
27-
Update: databaseSecretBackendStaticRoleWrite,
28-
Delete: databaseSecretBackendStaticRoleDelete,
29-
Exists: databaseSecretBackendStaticRoleExists,
33+
CreateContext: databaseSecretBackendStaticRoleWrite,
34+
ReadContext: provider.ReadContextWrapper(databaseSecretBackendStaticRoleRead),
35+
UpdateContext: databaseSecretBackendStaticRoleWrite,
36+
DeleteContext: databaseSecretBackendStaticRoleDelete,
3037
Importer: &schema.ResourceImporter{
31-
State: schema.ImportStatePassthrough,
38+
StateContext: schema.ImportStatePassthroughContext,
3239
},
3340

3441
Schema: map[string]*schema.Schema{
35-
"name": {
42+
consts.FieldName: {
3643
Type: schema.TypeString,
3744
Required: true,
3845
ForceNew: true,
3946
Description: "Unique name for the static role.",
4047
},
41-
"backend": {
48+
consts.FieldBackend: {
4249
Type: schema.TypeString,
4350
Required: true,
4451
ForceNew: true,
4552
Description: "The path of the Database Secret Backend the role belongs to.",
4653
},
47-
"username": {
54+
consts.FieldUsername: {
4855
Type: schema.TypeString,
4956
Required: true,
5057
ForceNew: true,
5158
Description: "The database username that this role corresponds to.",
5259
},
53-
"rotation_period": {
60+
consts.FieldRotationPeriod: {
5461
Type: schema.TypeInt,
55-
Required: true,
62+
Optional: true,
5663
Description: "The amount of time Vault should wait before rotating the password, in seconds.",
5764
ValidateFunc: func(v interface{}, k string) (ws []string, errs []error) {
5865
value := v.(int)
@@ -62,13 +69,24 @@ func databaseSecretBackendStaticRoleResource() *schema.Resource {
6269
return
6370
},
6471
},
65-
"db_name": {
72+
consts.FieldRotationSchedule: {
73+
Type: schema.TypeString,
74+
Optional: true,
75+
Description: "A cron-style string that will define the schedule on which rotations should occur.",
76+
},
77+
consts.FieldRotationWindow: {
78+
Type: schema.TypeInt,
79+
Optional: true,
80+
Description: "The amount of time in seconds in which the rotations are allowed to occur starting " +
81+
"from a given rotation_schedule.",
82+
},
83+
consts.FieldDBName: {
6684
Type: schema.TypeString,
6785
Required: true,
6886
ForceNew: true,
6987
Description: "Database connection to use for this role.",
7088
},
71-
"rotation_statements": {
89+
consts.FieldRotationStatements: {
7290
Type: schema.TypeList,
7391
Optional: true,
7492
Elem: &schema.Schema{Type: schema.TypeString},
@@ -78,43 +96,56 @@ func databaseSecretBackendStaticRoleResource() *schema.Resource {
7896
}
7997
}
8098

81-
func databaseSecretBackendStaticRoleWrite(d *schema.ResourceData, meta interface{}) error {
99+
func databaseSecretBackendStaticRoleWrite(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
82100
client, e := provider.GetClient(d, meta)
83101
if e != nil {
84-
return e
102+
return diag.FromErr(e)
85103
}
86104

87-
backend := d.Get("backend").(string)
88-
name := d.Get("name").(string)
105+
backend := d.Get(consts.FieldBackend).(string)
106+
name := d.Get(consts.FieldName).(string)
89107

90108
path := databaseSecretBackendStaticRolePath(backend, name)
91109

92110
data := map[string]interface{}{
93-
"username": d.Get("username"),
94-
"rotation_period": d.Get("rotation_period"),
95-
"db_name": d.Get("db_name"),
111+
"username": d.Get(consts.FieldUsername),
112+
"db_name": d.Get(consts.FieldDBName),
96113
"rotation_statements": []string{},
97114
}
98115

99-
if v, ok := d.GetOkExists("rotation_statements"); ok && v != "" {
100-
data["rotation_statements"] = v
116+
useAPIVer115 := provider.IsAPISupported(meta, provider.VaultVersion115)
117+
if useAPIVer115 {
118+
if v, ok := d.GetOk(consts.FieldRotationSchedule); ok && v != "" {
119+
data[consts.FieldRotationSchedule] = v
120+
}
121+
if v, ok := d.GetOk(consts.FieldRotationWindow); ok && v != "" {
122+
data[consts.FieldRotationWindow] = v
123+
}
124+
}
125+
126+
if v, ok := d.GetOk(consts.FieldRotationStatements); ok && v != "" {
127+
data[consts.FieldRotationStatements] = v
128+
}
129+
130+
if v, ok := d.GetOk(consts.FieldRotationPeriod); ok && v != "" {
131+
data[consts.FieldRotationPeriod] = v
101132
}
102133

103134
log.Printf("[DEBUG] Creating static role %q on database backend %q", name, backend)
104135
_, err := client.Logical().Write(path, data)
105136
if err != nil {
106-
return fmt.Errorf("error creating static role %q for backend %q: %s", name, backend, err)
137+
return diag.Errorf("error creating static role %q for backend %q: %s", name, backend, err)
107138
}
108139
log.Printf("[DEBUG] Created static role %q on AWS backend %q", name, backend)
109140

110141
d.SetId(path)
111-
return databaseSecretBackendStaticRoleRead(d, meta)
142+
return databaseSecretBackendStaticRoleRead(ctx, d, meta)
112143
}
113144

114-
func databaseSecretBackendStaticRoleRead(d *schema.ResourceData, meta interface{}) error {
145+
func databaseSecretBackendStaticRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
115146
client, e := provider.GetClient(d, meta)
116147
if e != nil {
117-
return e
148+
return diag.FromErr(e)
118149
}
119150

120151
path := d.Id()
@@ -123,20 +154,20 @@ func databaseSecretBackendStaticRoleRead(d *schema.ResourceData, meta interface{
123154
if err != nil {
124155
log.Printf("[WARN] Removing database static role %q because its ID is invalid", path)
125156
d.SetId("")
126-
return fmt.Errorf("invalid static role ID %q: %s", path, err)
157+
return diag.Errorf("invalid static role ID %q: %s", path, err)
127158
}
128159

129160
backend, err := databaseSecretBackendStaticRoleBackendFromPath(path)
130161
if err != nil {
131162
log.Printf("[WARN] Removing database static role %q because its ID is invalid", path)
132163
d.SetId("")
133-
return fmt.Errorf("invalid static role ID %q: %s", path, err)
164+
return diag.Errorf("invalid static role ID %q: %s", path, err)
134165
}
135166

136167
log.Printf("[DEBUG] Reading static role from %q", path)
137168
role, err := client.Logical().Read(path)
138169
if err != nil {
139-
return fmt.Errorf("error reading static role %q: %s", path, err)
170+
return diag.Errorf("error reading static role %q: %s", path, err)
140171
}
141172
log.Printf("[DEBUG] Read static role from %q", path)
142173
if role == nil {
@@ -145,67 +176,55 @@ func databaseSecretBackendStaticRoleRead(d *schema.ResourceData, meta interface{
145176
return nil
146177
}
147178

148-
d.Set("backend", backend)
149-
d.Set("name", name)
150-
d.Set("username", role.Data["username"])
151-
d.Set("db_name", role.Data["db_name"])
179+
if err := d.Set(consts.FieldBackend, backend); err != nil {
180+
return diag.FromErr(err)
181+
}
152182

153-
if v, ok := role.Data["rotation_period"]; ok {
154-
n, err := v.(json.Number).Int64()
155-
if err != nil {
156-
return fmt.Errorf("unexpected value %q for rotation_period of %q", v, path)
157-
}
158-
d.Set("rotation_period", n)
183+
if err := d.Set(consts.FieldName, name); err != nil {
184+
return diag.FromErr(err)
159185
}
160186

161-
var rotation []string
162-
if rotationStr, ok := role.Data["rotation_statements"].(string); ok {
163-
rotation = append(rotation, rotationStr)
164-
} else if rotations, ok := role.Data["rotation_statements"].([]interface{}); ok {
165-
for _, cr := range rotations {
166-
rotation = append(rotation, cr.(string))
187+
if err := d.Set(consts.FieldUsername, role.Data[consts.FieldUsername]); err != nil {
188+
return diag.FromErr(err)
189+
}
190+
191+
useAPIVer115 := provider.IsAPISupported(meta, provider.VaultVersion115)
192+
if useAPIVer115 {
193+
if err := d.Set(consts.FieldRotationSchedule, role.Data[consts.FieldRotationSchedule]); err != nil {
194+
return diag.FromErr(err)
195+
}
196+
if err := d.Set(consts.FieldRotationWindow, role.Data[consts.FieldRotationWindow]); err != nil {
197+
return diag.FromErr(err)
167198
}
168199
}
169-
err = d.Set("rotation_statements", rotation)
170-
if err != nil {
171-
return fmt.Errorf("unexpected value %q for rotation_statements of %s: %s", rotation, path, err)
200+
201+
for _, k := range staticRoleFields {
202+
if v, ok := role.Data[k]; ok {
203+
if err := d.Set(k, v); err != nil {
204+
return diag.FromErr(err)
205+
}
206+
}
172207
}
173208

174209
return nil
175210
}
176211

177-
func databaseSecretBackendStaticRoleDelete(d *schema.ResourceData, meta interface{}) error {
212+
func databaseSecretBackendStaticRoleDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
178213
client, e := provider.GetClient(d, meta)
179214
if e != nil {
180-
return e
215+
return diag.FromErr(e)
181216
}
182217

183218
path := d.Id()
184219
log.Printf("[DEBUG] Deleting static role %q", path)
185220
_, err := client.Logical().Delete(path)
186221
if err != nil {
187-
return fmt.Errorf("error deleting static role %q: %s", path, err)
222+
return diag.Errorf("error deleting static role %q: %s", path, err)
188223
}
189224
log.Printf("[DEBUG] Deleted static role %q", path)
190225
return nil
191226
}
192227

193-
func databaseSecretBackendStaticRoleExists(d *schema.ResourceData, meta interface{}) (bool, error) {
194-
client, e := provider.GetClient(d, meta)
195-
if e != nil {
196-
return false, e
197-
}
198-
199-
path := d.Id()
200-
log.Printf("[DEBUG] Checking if %q exists", path)
201-
role, err := client.Logical().Read(path)
202-
if err != nil {
203-
return true, fmt.Errorf("error checking if %q exists: %s", path, err)
204-
}
205-
log.Printf("[DEBUG] Checked if %q exists", path)
206-
return role != nil, nil
207-
}
208-
209228
func databaseSecretBackendStaticRolePath(backend, name string) string {
210229
return strings.Trim(backend, "/") + "/static-roles/" + strings.Trim(name, "/")
211230
}

0 commit comments

Comments
 (0)