Skip to content

Commit 008a2d8

Browse files
authored
Merge pull request #23745 from DrFaust92/athena-database
r/athena_database - add `acl_configuration` and `expected_bucket_owner` args
2 parents f127a11 + 9fd4b4d commit 008a2d8

File tree

5 files changed

+444
-185
lines changed

5 files changed

+444
-185
lines changed

.changelog/23745.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
```release-note:enhancement
2+
resource/aws_athena_database: Add `acl_configuration` and `expected_bucket_owner` arguments
3+
```
4+
5+
```release-note:bug
6+
resource/aws_athena_database: Remove from state on resource Read if deleted outside of Terraform
7+
```
8+
9+
```release-note:enhancement
10+
resource/aws_athena_database: Do not recreate the resource if `bucket` changes
11+
```
12+
13+
```release-note:enhancement
14+
resource/aws_athena_database: Add `comment` argument to support database descriptions
15+
```

internal/service/athena/database.go

+129-71
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package athena
22

33
import (
44
"fmt"
5+
"log"
56
"regexp"
67
"strings"
78
"time"
89

910
"github.com/aws/aws-sdk-go/aws"
1011
"github.com/aws/aws-sdk-go/service/athena"
12+
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
1113
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1315
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -18,161 +20,217 @@ func ResourceDatabase() *schema.Resource {
1820
return &schema.Resource{
1921
Create: resourceDatabaseCreate,
2022
Read: resourceDatabaseRead,
21-
Update: resourceDatabaseUpdate,
23+
Update: schema.Noop,
2224
Delete: resourceDatabaseDelete,
2325

2426
Schema: map[string]*schema.Schema{
25-
"name": {
26-
Type: schema.TypeString,
27-
Required: true,
28-
ForceNew: true,
29-
ValidateFunc: validation.StringMatch(regexp.MustCompile("^[_a-z0-9]+$"), "must be lowercase letters, numbers, or underscore ('_')"),
27+
"acl_configuration": {
28+
Type: schema.TypeList,
29+
Optional: true,
30+
ForceNew: true,
31+
MaxItems: 1,
32+
Elem: &schema.Resource{
33+
Schema: map[string]*schema.Schema{
34+
"s3_acl_option": {
35+
Type: schema.TypeString,
36+
Required: true,
37+
ValidateFunc: validation.StringInSlice(athena.S3AclOption_Values(), false),
38+
ForceNew: true,
39+
},
40+
},
41+
},
3042
},
3143
"bucket": {
3244
Type: schema.TypeString,
33-
Required: true,
34-
ForceNew: true,
45+
Optional: true,
3546
},
36-
"force_destroy": {
37-
Type: schema.TypeBool,
47+
"comment": {
48+
Type: schema.TypeString,
3849
Optional: true,
39-
Default: false,
50+
ForceNew: true,
4051
},
4152
"encryption_configuration": {
4253
Type: schema.TypeList,
4354
Optional: true,
55+
ForceNew: true,
4456
MaxItems: 1,
4557
Elem: &schema.Resource{
4658
Schema: map[string]*schema.Schema{
59+
"encryption_option": {
60+
Type: schema.TypeString,
61+
Required: true,
62+
ValidateFunc: validation.StringInSlice(athena.EncryptionOption_Values(), false),
63+
ForceNew: true,
64+
},
4765
"kms_key": {
4866
Type: schema.TypeString,
4967
Optional: true,
50-
},
51-
"encryption_option": {
52-
Type: schema.TypeString,
53-
Required: true,
54-
ValidateFunc: validation.StringInSlice([]string{
55-
athena.EncryptionOptionCseKms,
56-
athena.EncryptionOptionSseKms,
57-
athena.EncryptionOptionSseS3,
58-
}, false),
68+
ForceNew: true,
5969
},
6070
},
6171
},
6272
},
73+
"expected_bucket_owner": {
74+
Type: schema.TypeString,
75+
Optional: true,
76+
ForceNew: true,
77+
},
78+
"force_destroy": {
79+
Type: schema.TypeBool,
80+
Optional: true,
81+
Default: false,
82+
},
83+
"name": {
84+
Type: schema.TypeString,
85+
Required: true,
86+
ForceNew: true,
87+
ValidateFunc: validation.StringMatch(regexp.MustCompile("^[_a-z0-9]+$"), "must be lowercase letters, numbers, or underscore ('_')"),
88+
},
6389
},
6490
}
6591
}
6692

67-
func expandAthenaResultConfiguration(bucket string, encryptionConfigurationList []interface{}) *athena.ResultConfiguration {
68-
resultConfig := athena.ResultConfiguration{
69-
OutputLocation: aws.String("s3://" + bucket),
70-
}
71-
72-
if len(encryptionConfigurationList) <= 0 {
73-
return &resultConfig
74-
}
75-
76-
data := encryptionConfigurationList[0].(map[string]interface{})
77-
keyType := data["encryption_option"].(string)
78-
keyID := data["kms_key"].(string)
93+
func resourceDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
94+
conn := meta.(*conns.AWSClient).AthenaConn
7995

80-
encryptionConfig := athena.EncryptionConfiguration{
81-
EncryptionOption: aws.String(keyType),
82-
}
96+
name := d.Get("name").(string)
97+
var queryString string
8398

84-
if len(keyID) > 0 {
85-
encryptionConfig.KmsKey = aws.String(keyID)
99+
if v, ok := d.GetOk("comment"); ok {
100+
queryString = fmt.Sprintf("create database `%[1]s` comment '%[2]s';", name, strings.Replace(v.(string), "'", "\\'", -1))
101+
} else {
102+
queryString = fmt.Sprintf("create database `%[1]s`;", name)
86103
}
87104

88-
resultConfig.EncryptionConfiguration = &encryptionConfig
89-
90-
return &resultConfig
91-
}
92-
93-
func resourceDatabaseCreate(d *schema.ResourceData, meta interface{}) error {
94-
conn := meta.(*conns.AWSClient).AthenaConn
95-
96105
input := &athena.StartQueryExecutionInput{
97-
QueryString: aws.String(fmt.Sprintf("create database `%s`;", d.Get("name").(string))),
98-
ResultConfiguration: expandAthenaResultConfiguration(d.Get("bucket").(string), d.Get("encryption_configuration").([]interface{})),
106+
QueryString: aws.String(queryString),
107+
ResultConfiguration: expandAthenaResultConfiguration(d),
99108
}
100109

101110
resp, err := conn.StartQueryExecution(input)
111+
102112
if err != nil {
103-
return err
113+
return fmt.Errorf("error starting Athena Database (%s) query execution: %w", name, err)
104114
}
105115

106-
if err := executeAndExpectNoRowsWhenCreate(*resp.QueryExecutionId, conn); err != nil {
116+
if err := executeAndExpectNoRows(*resp.QueryExecutionId, "create", conn); err != nil {
107117
return err
108118
}
109-
d.SetId(d.Get("name").(string))
119+
120+
d.SetId(name)
121+
110122
return resourceDatabaseRead(d, meta)
111123
}
112124

113125
func resourceDatabaseRead(d *schema.ResourceData, meta interface{}) error {
114126
conn := meta.(*conns.AWSClient).AthenaConn
115127

116128
input := &athena.GetDatabaseInput{
117-
DatabaseName: aws.String(d.Get("name").(string)),
129+
DatabaseName: aws.String(d.Id()),
118130
CatalogName: aws.String("AwsDataCatalog"),
119131
}
120-
_, err := conn.GetDatabase(input)
132+
res, err := conn.GetDatabase(input)
133+
134+
if tfawserr.ErrMessageContains(err, athena.ErrCodeMetadataException, "not found") && !d.IsNewResource() {
135+
log.Printf("[WARN] Athena Database (%s) not found, removing from state", d.Id())
136+
d.SetId("")
137+
return nil
138+
}
139+
121140
if err != nil {
122-
return err
141+
return fmt.Errorf("error reading Athena Database (%s): %w", d.Id(), err)
123142
}
124-
return nil
125-
}
126143

127-
func resourceDatabaseUpdate(d *schema.ResourceData, meta interface{}) error {
128-
return resourceDatabaseRead(d, meta)
144+
db := res.Database
145+
146+
d.Set("name", db.Name)
147+
148+
return nil
129149
}
130150

131151
func resourceDatabaseDelete(d *schema.ResourceData, meta interface{}) error {
132152
conn := meta.(*conns.AWSClient).AthenaConn
133153

134-
name := d.Get("name").(string)
135-
136-
queryString := fmt.Sprintf("drop database `%s`", name)
154+
queryString := fmt.Sprintf("drop database `%s`", d.Id())
137155
if d.Get("force_destroy").(bool) {
138156
queryString += " cascade"
139157
}
140158
queryString += ";"
141159

142160
input := &athena.StartQueryExecutionInput{
143161
QueryString: aws.String(queryString),
144-
ResultConfiguration: expandAthenaResultConfiguration(d.Get("bucket").(string), d.Get("encryption_configuration").([]interface{})),
162+
ResultConfiguration: expandAthenaResultConfiguration(d),
145163
}
146164

147165
resp, err := conn.StartQueryExecution(input)
148166
if err != nil {
149167
return err
150168
}
151169

152-
if err := executeAndExpectNoRowsWhenDrop(*resp.QueryExecutionId, conn); err != nil {
170+
if err := executeAndExpectNoRows(*resp.QueryExecutionId, "delete", conn); err != nil {
153171
return err
154172
}
173+
155174
return nil
156175
}
157176

158-
func executeAndExpectNoRowsWhenCreate(qeid string, conn *athena.Athena) error {
159-
rs, err := QueryExecutionResult(qeid, conn)
160-
if err != nil {
161-
return err
177+
func expandAthenaResultConfiguration(d *schema.ResourceData) *athena.ResultConfiguration {
178+
179+
resultConfig := &athena.ResultConfiguration{
180+
OutputLocation: aws.String("s3://" + d.Get("bucket").(string)),
181+
EncryptionConfiguration: expandAthenaResultConfigurationEncryptionConfig(d.Get("encryption_configuration").([]interface{})),
162182
}
163-
if len(rs.Rows) != 0 {
164-
return fmt.Errorf("Athena create database, unexpected query result: %s", flattenAthenaResultSet(rs))
183+
184+
if v, ok := d.GetOk("expected_bucket_owner"); ok {
185+
resultConfig.ExpectedBucketOwner = aws.String(v.(string))
165186
}
166-
return nil
187+
188+
if v, ok := d.GetOk("acl_configuration"); ok && len(v.([]interface{})) > 0 {
189+
resultConfig.AclConfiguration = expandAthenaResultConfigurationAclConfig(v.([]interface{}))
190+
}
191+
192+
return resultConfig
193+
}
194+
195+
func expandAthenaResultConfigurationEncryptionConfig(config []interface{}) *athena.EncryptionConfiguration {
196+
if len(config) <= 0 {
197+
return nil
198+
}
199+
200+
data := config[0].(map[string]interface{})
201+
202+
encryptionConfig := &athena.EncryptionConfiguration{
203+
EncryptionOption: aws.String(data["encryption_option"].(string)),
204+
}
205+
206+
if v, ok := data["kms_key"].(string); ok && v != "" {
207+
encryptionConfig.KmsKey = aws.String(v)
208+
}
209+
210+
return encryptionConfig
211+
}
212+
213+
func expandAthenaResultConfigurationAclConfig(config []interface{}) *athena.AclConfiguration {
214+
if len(config) <= 0 {
215+
return nil
216+
}
217+
218+
data := config[0].(map[string]interface{})
219+
220+
encryptionConfig := &athena.AclConfiguration{
221+
S3AclOption: aws.String(data["s3_acl_option"].(string)),
222+
}
223+
224+
return encryptionConfig
167225
}
168226

169-
func executeAndExpectNoRowsWhenDrop(qeid string, conn *athena.Athena) error {
227+
func executeAndExpectNoRows(qeid, action string, conn *athena.Athena) error {
170228
rs, err := QueryExecutionResult(qeid, conn)
171229
if err != nil {
172230
return err
173231
}
174232
if len(rs.Rows) != 0 {
175-
return fmt.Errorf("Athena drop database, unexpected query result: %s", flattenAthenaResultSet(rs))
233+
return fmt.Errorf("Athena %s database, unexpected query result: %s", action, flattenAthenaResultSet(rs))
176234
}
177235
return nil
178236
}

0 commit comments

Comments
 (0)