1
1
package s3control
2
2
3
3
import (
4
+ "context"
4
5
"fmt"
5
6
"log"
6
7
"regexp"
@@ -11,6 +12,7 @@ import (
11
12
"github.com/aws/aws-sdk-go/aws/arn"
12
13
"github.com/aws/aws-sdk-go/service/s3control"
13
14
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
15
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
14
16
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
15
17
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
16
18
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -25,12 +27,16 @@ const (
25
27
bucketStatePropagationTimeout = 5 * time .Minute
26
28
)
27
29
28
- func ResourceBucket () * schema.Resource {
30
+ func init () {
31
+ _sp .registerSDKResourceFactory ("aws_s3control_bucket" , resourceBucket )
32
+ }
33
+
34
+ func resourceBucket () * schema.Resource {
29
35
return & schema.Resource {
30
- Create : resourceBucketCreate ,
31
- Read : resourceBucketRead ,
32
- Update : resourceBucketUpdate ,
33
- Delete : resourceBucketDelete ,
36
+ CreateWithoutTimeout : resourceBucketCreate ,
37
+ ReadWithoutTimeout : resourceBucketRead ,
38
+ UpdateWithoutTimeout : resourceBucketUpdate ,
39
+ DeleteWithoutTimeout : resourceBucketDelete ,
34
40
35
41
Importer : & schema.ResourceImporter {
36
42
State : schema .ImportStatePassthrough ,
@@ -74,135 +80,113 @@ func ResourceBucket() *schema.Resource {
74
80
}
75
81
}
76
82
77
- func resourceBucketCreate (d * schema.ResourceData , meta interface {}) error {
83
+ func resourceBucketCreate (ctx context. Context , d * schema.ResourceData , meta interface {}) diag. Diagnostics {
78
84
conn := meta .(* conns.AWSClient ).S3ControlConn ()
79
85
defaultTagsConfig := meta .(* conns.AWSClient ).DefaultTagsConfig
80
86
tags := defaultTagsConfig .MergeTags (tftags .New (d .Get ("tags" ).(map [string ]interface {})))
81
87
82
88
bucket := d .Get ("bucket" ).(string )
83
-
84
89
input := & s3control.CreateBucketInput {
85
90
Bucket : aws .String (bucket ),
86
91
OutpostId : aws .String (d .Get ("outpost_id" ).(string )),
87
92
}
88
93
89
- output , err := conn .CreateBucket ( input )
94
+ output , err := conn .CreateBucketWithContext ( ctx , input )
90
95
91
96
if err != nil {
92
- return fmt .Errorf ("error creating S3 Control Bucket (%s): %w" , bucket , err )
93
- }
94
-
95
- if output == nil {
96
- return fmt .Errorf ("error creating S3 Control Bucket (%s): empty response" , bucket )
97
+ return diag .Errorf ("creating S3 Control Bucket (%s): %s" , bucket , err )
97
98
}
98
99
99
100
d .SetId (aws .StringValue (output .BucketArn ))
100
101
101
102
if len (tags ) > 0 {
102
- if err := bucketUpdateTags (conn , d .Id (), nil , tags ); err != nil {
103
- return fmt .Errorf ("error adding S3 Control Bucket (%s) tags: %w " , d .Id (), err )
103
+ if err := bucketUpdateTags (ctx , conn , d .Id (), nil , tags ); err != nil {
104
+ return diag .Errorf ("adding S3 Control Bucket (%s) tags: %s " , d .Id (), err )
104
105
}
105
106
}
106
107
107
- return resourceBucketRead (d , meta )
108
+ return resourceBucketRead (ctx , d , meta )
108
109
}
109
110
110
- func resourceBucketRead (d * schema.ResourceData , meta interface {}) error {
111
+ func resourceBucketRead (ctx context. Context , d * schema.ResourceData , meta interface {}) diag. Diagnostics {
111
112
conn := meta .(* conns.AWSClient ).S3ControlConn ()
112
113
defaultTagsConfig := meta .(* conns.AWSClient ).DefaultTagsConfig
113
114
ignoreTagsConfig := meta .(* conns.AWSClient ).IgnoreTagsConfig
114
115
115
116
parsedArn , err := arn .Parse (d .Id ())
116
117
117
118
if err != nil {
118
- return fmt . Errorf ( "error parsing S3 Control Bucket ARN (%s): %w" , d . Id (), err )
119
+ return diag . FromErr ( err )
119
120
}
120
121
121
122
// ARN resource format: outpost/<outpost-id>/bucket/<my-bucket-name>
122
123
arnResourceParts := strings .Split (parsedArn .Resource , "/" )
123
124
124
125
if parsedArn .AccountID == "" || len (arnResourceParts ) != 4 {
125
- return fmt .Errorf ("error parsing S3 Control Bucket ARN (%s): unknown format" , d .Id ())
126
- }
127
-
128
- input := & s3control.GetBucketInput {
129
- AccountId : aws .String (parsedArn .AccountID ),
130
- Bucket : aws .String (d .Id ()),
126
+ return diag .Errorf ("parsing S3 Control Bucket ARN (%s): unknown format" , d .Id ())
131
127
}
132
128
133
- output , err := conn .GetBucket (input )
134
-
135
- if ! d .IsNewResource () && tfawserr .ErrCodeEquals (err , "NoSuchBucket" ) {
136
- log .Printf ("[WARN] S3 Control Bucket (%s) not found, removing from state" , d .Id ())
137
- d .SetId ("" )
138
- return nil
139
- }
129
+ output , err := FindBucketByTwoPartKey (ctx , conn , parsedArn .AccountID , d .Id ())
140
130
141
- if ! d .IsNewResource () && tfawserr . ErrCodeEquals (err , "NoSuchOutpost" ) {
131
+ if ! d .IsNewResource () && tfresource . NotFound (err ) {
142
132
log .Printf ("[WARN] S3 Control Bucket (%s) not found, removing from state" , d .Id ())
143
133
d .SetId ("" )
144
134
return nil
145
135
}
146
136
147
137
if err != nil {
148
- return fmt .Errorf ("error reading S3 Control Bucket (%s): %w" , d .Id (), err )
149
- }
150
-
151
- if output == nil {
152
- return fmt .Errorf ("error reading S3 Control Bucket (%s): empty response" , d .Id ())
138
+ return diag .Errorf ("reading S3 Control Bucket (%s): %s" , d .Id (), err )
153
139
}
154
140
155
141
d .Set ("arn" , d .Id ())
156
142
d .Set ("bucket" , output .Bucket )
157
-
158
143
if output .CreationDate != nil {
159
144
d .Set ("creation_date" , aws .TimeValue (output .CreationDate ).Format (time .RFC3339 ))
160
145
}
161
-
162
146
d .Set ("outpost_id" , arnResourceParts [1 ])
163
147
d .Set ("public_access_block_enabled" , output .PublicAccessBlockEnabled )
164
148
165
- tags , err := bucketListTags (conn , d .Id ())
149
+ tags , err := bucketListTags (ctx , conn , d .Id ())
166
150
167
151
if err != nil {
168
- return fmt .Errorf ("error listing tags for S3 Control Bucket (%s): %w " , d .Id (), err )
152
+ return diag .Errorf ("listing tags for S3 Control Bucket (%s): %s " , d .Id (), err )
169
153
}
170
154
171
155
tags = tags .IgnoreAWS ().IgnoreConfig (ignoreTagsConfig )
172
156
173
157
//lintignore:AWSR002
174
158
if err := d .Set ("tags" , tags .RemoveDefaultConfig (defaultTagsConfig ).Map ()); err != nil {
175
- return fmt .Errorf ("error setting tags: %w " , err )
159
+ return diag .Errorf ("setting tags: %s " , err )
176
160
}
177
161
178
162
if err := d .Set ("tags_all" , tags .Map ()); err != nil {
179
- return fmt .Errorf ("error setting tags_all: %w " , err )
163
+ return diag .Errorf ("setting tags_all: %s " , err )
180
164
}
181
165
182
166
return nil
183
167
}
184
168
185
- func resourceBucketUpdate (d * schema.ResourceData , meta interface {}) error {
169
+ func resourceBucketUpdate (ctx context. Context , d * schema.ResourceData , meta interface {}) diag. Diagnostics {
186
170
conn := meta .(* conns.AWSClient ).S3ControlConn ()
187
171
188
172
if d .HasChange ("tags_all" ) {
189
173
o , n := d .GetChange ("tags_all" )
190
174
191
- if err := bucketUpdateTags (conn , d .Id (), o , n ); err != nil {
192
- return fmt .Errorf ("error updating S3 Control Bucket (%s) tags: %w " , d .Id (), err )
175
+ if err := bucketUpdateTags (ctx , conn , d .Id (), o , n ); err != nil {
176
+ return diag .Errorf ("updating S3 Control Bucket (%s) tags: %s " , d .Id (), err )
193
177
}
194
178
}
195
179
196
- return resourceBucketRead (d , meta )
180
+ return resourceBucketRead (ctx , d , meta )
197
181
}
198
182
199
- func resourceBucketDelete (d * schema.ResourceData , meta interface {}) error {
183
+ func resourceBucketDelete (ctx context. Context , d * schema.ResourceData , meta interface {}) diag. Diagnostics {
200
184
conn := meta .(* conns.AWSClient ).S3ControlConn ()
201
185
202
186
parsedArn , err := arn .Parse (d .Id ())
203
187
204
188
if err != nil {
205
- return fmt . Errorf ( "error parsing S3 Control Bucket ARN (%s): %w" , d . Id (), err )
189
+ return diag . FromErr ( err )
206
190
}
207
191
208
192
input := & s3control.DeleteBucketInput {
@@ -213,34 +197,123 @@ func resourceBucketDelete(d *schema.ResourceData, meta interface{}) error {
213
197
// S3 Control Bucket have a backend state which cannot be checked so this error
214
198
// can occur on deletion:
215
199
// InvalidBucketState: Bucket is in an invalid state
216
- err = resource .Retry (bucketStatePropagationTimeout , func () * resource.RetryError {
217
- _ , err := conn .DeleteBucket (input )
200
+ log .Printf ("[DEBUG] Deleting S3 Control Bucket: %s" , d .Id ())
201
+ _ , err = tfresource .RetryWhenAWSErrCodeEqualsContext (ctx , bucketStatePropagationTimeout , func () (interface {}, error ) {
202
+ return conn .DeleteBucketWithContext (ctx , input )
203
+ }, errCodeInvalidBucketState )
218
204
219
- if tfawserr .ErrCodeEquals (err , "InvalidBucketState" ) {
220
- return resource . RetryableError ( err )
221
- }
205
+ if tfawserr .ErrCodeEquals (err , errCodeNoSuchBucket , errCodeNoSuchOutpost ) {
206
+ return nil
207
+ }
222
208
223
- if err != nil {
224
- return resource .NonRetryableError (err )
209
+ if err != nil {
210
+ return diag .Errorf ("deleting S3 Control Bucket (%s): %s" , d .Id (), err )
211
+ }
212
+
213
+ return nil
214
+ }
215
+
216
+ func FindBucketByTwoPartKey (ctx context.Context , conn * s3control.S3Control , accountID , bucket string ) (* s3control.GetBucketOutput , error ) {
217
+ input := & s3control.GetBucketInput {
218
+ AccountId : aws .String (accountID ),
219
+ Bucket : aws .String (bucket ),
220
+ }
221
+
222
+ output , err := conn .GetBucketWithContext (ctx , input )
223
+
224
+ if tfawserr .ErrCodeEquals (err , errCodeNoSuchBucket , errCodeNoSuchOutpost ) {
225
+ return nil , & resource.NotFoundError {
226
+ LastError : err ,
227
+ LastRequest : input ,
225
228
}
229
+ }
226
230
227
- return nil
228
- })
231
+ if err != nil {
232
+ return nil , err
233
+ }
229
234
230
- if tfresource . TimedOut ( err ) {
231
- _ , err = conn . DeleteBucket (input )
235
+ if output == nil {
236
+ return nil , tfresource . NewEmptyResultError (input )
232
237
}
233
238
234
- if tfawserr .ErrCodeEquals (err , "NoSuchBucket" ) {
235
- return nil
239
+ return output , nil
240
+ }
241
+
242
+ // Custom S3control tagging functions using similar formatting as other service generated code.
243
+
244
+ // bucketListTags lists S3control bucket tags.
245
+ // The identifier is the bucket ARN.
246
+ func bucketListTags (ctx context.Context , conn * s3control.S3Control , identifier string ) (tftags.KeyValueTags , error ) {
247
+ parsedArn , err := arn .Parse (identifier )
248
+
249
+ if err != nil {
250
+ return tftags .New (nil ), err
236
251
}
237
252
238
- if tfawserr .ErrCodeEquals (err , "NoSuchOutpost" ) {
239
- return nil
253
+ input := & s3control.GetBucketTaggingInput {
254
+ AccountId : aws .String (parsedArn .AccountID ),
255
+ Bucket : aws .String (identifier ),
256
+ }
257
+
258
+ output , err := conn .GetBucketTaggingWithContext (ctx , input )
259
+
260
+ if tfawserr .ErrCodeEquals (err , errCodeNoSuchTagSet ) {
261
+ return tftags .New (nil ), nil
262
+ }
263
+
264
+ if err != nil {
265
+ return tftags .New (nil ), err
266
+ }
267
+
268
+ return KeyValueTags (output .TagSet ), nil
269
+ }
270
+
271
+ // bucketUpdateTags updates S3control bucket tags.
272
+ // The identifier is the bucket ARN.
273
+ func bucketUpdateTags (ctx context.Context , conn * s3control.S3Control , identifier string , oldTagsMap interface {}, newTagsMap interface {}) error {
274
+ parsedArn , err := arn .Parse (identifier )
275
+
276
+ if err != nil {
277
+ return err
240
278
}
241
279
280
+ oldTags := tftags .New (oldTagsMap )
281
+ newTags := tftags .New (newTagsMap )
282
+
283
+ // We need to also consider any existing ignored tags.
284
+ allTags , err := bucketListTags (ctx , conn , identifier )
285
+
242
286
if err != nil {
243
- return fmt .Errorf ("error deleting S3 Control Bucket (%s): %w" , d .Id (), err )
287
+ return fmt .Errorf ("listing resource tags (%s): %w" , identifier , err )
288
+ }
289
+
290
+ ignoredTags := allTags .Ignore (oldTags ).Ignore (newTags )
291
+
292
+ if len (newTags )+ len (ignoredTags ) > 0 {
293
+ input := & s3control.PutBucketTaggingInput {
294
+ AccountId : aws .String (parsedArn .AccountID ),
295
+ Bucket : aws .String (identifier ),
296
+ Tagging : & s3control.Tagging {
297
+ TagSet : Tags (newTags .Merge (ignoredTags )),
298
+ },
299
+ }
300
+
301
+ _ , err := conn .PutBucketTaggingWithContext (ctx , input )
302
+
303
+ if err != nil {
304
+ return fmt .Errorf ("setting resource tags (%s): %s" , identifier , err )
305
+ }
306
+ } else if len (oldTags ) > 0 && len (ignoredTags ) == 0 {
307
+ input := & s3control.DeleteBucketTaggingInput {
308
+ AccountId : aws .String (parsedArn .AccountID ),
309
+ Bucket : aws .String (identifier ),
310
+ }
311
+
312
+ _ , err := conn .DeleteBucketTaggingWithContext (ctx , input )
313
+
314
+ if err != nil {
315
+ return fmt .Errorf ("deleting resource tags (%s): %s" , identifier , err )
316
+ }
244
317
}
245
318
246
319
return nil
0 commit comments