@@ -2,11 +2,13 @@ package gridscale
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "log"
78 "time"
89
910 "github.com/aws/aws-sdk-go/aws"
11+ "github.com/aws/aws-sdk-go/aws/awserr"
1012 "github.com/aws/aws-sdk-go/aws/credentials"
1113 "github.com/aws/aws-sdk-go/aws/session"
1214 "github.com/aws/aws-sdk-go/service/s3"
@@ -31,6 +33,7 @@ func resourceGridscaleBucket() *schema.Resource {
3133 return & schema.Resource {
3234 Create : resourceGridscaleBucketCreate ,
3335 Read : resourceGridscaleBucketRead ,
36+ Update : resourceGridscaleBucketUpdate ,
3437 Delete : resourceGridscaleBucketDelete ,
3538 Importer : & schema.ResourceImporter {
3639 State : schema .ImportStatePassthrough ,
@@ -63,15 +66,108 @@ func resourceGridscaleBucket() *schema.Resource {
6366 ForceNew : true ,
6467 Default : "gos3.io" ,
6568 },
69+ "lifecycle_rule" : {
70+ Type : schema .TypeList ,
71+ Optional : true ,
72+ Elem : & schema.Resource {
73+ Schema : map [string ]* schema.Schema {
74+ "id" : {
75+ Type : schema .TypeString ,
76+ Required : true ,
77+ },
78+ "enabled" : {
79+ Type : schema .TypeBool ,
80+ Required : true ,
81+ },
82+ "prefix" : {
83+ Type : schema .TypeString ,
84+ Optional : true ,
85+ },
86+ "expiration_days" : {
87+ Type : schema .TypeInt ,
88+ Optional : true ,
89+ Default : 365 ,
90+ },
91+ "noncurrent_version_expiration_days" : {
92+ Type : schema .TypeInt ,
93+ Optional : true ,
94+ Default : 365 ,
95+ },
96+ "incomplete_upload_expiration_days" : {
97+ Type : schema .TypeInt ,
98+ Optional : true ,
99+ Default : 3 ,
100+ },
101+ },
102+ },
103+ },
66104 },
67105 Timeouts : & schema.ResourceTimeout {
68106 Create : schema .DefaultTimeout (5 * time .Minute ),
69107 Delete : schema .DefaultTimeout (5 * time .Minute ),
108+ Update : schema .DefaultTimeout (5 * time .Minute ),
70109 },
71110 }
72111}
73112
74113func resourceGridscaleBucketRead (d * schema.ResourceData , meta interface {}) error {
114+ s3Host := d .Get ("s3_host" ).(string )
115+ accessKey := d .Get ("access_key" ).(string )
116+ secretKey := d .Get ("secret_key" ).(string )
117+ bucketName := d .Get ("bucket_name" ).(string )
118+
119+ s3Client := initS3Client (& gridscaleS3Provider {
120+ AccessKey : accessKey ,
121+ SecretKey : secretKey ,
122+ }, s3Host )
123+
124+ ctx , cancel := context .WithTimeout (context .Background (), d .Timeout (schema .TimeoutRead ))
125+ defer cancel ()
126+
127+ // Fetch lifecycle configuration
128+ output , err := s3Client .GetBucketLifecycleConfigurationWithContext (ctx , & s3.GetBucketLifecycleConfigurationInput {
129+ Bucket : aws .String (bucketName ),
130+ })
131+ if err != nil {
132+ var aerr awserr.Error
133+ if errors .As (err , & aerr ) && aerr .Code () == "NoSuchLifecycleConfiguration" {
134+ // If the error indicates no lifecycle configuration exists, set the lifecycle_rule attribute to nil
135+ d .Set ("lifecycle_rule" , nil )
136+ } else {
137+ // For any other error, return a formatted error message with context
138+ return fmt .Errorf ("error reading lifecycle configuration for bucket %s: %w" , bucketName , err )
139+ }
140+ } else {
141+ rules := []map [string ]interface {}{}
142+ for _ , rule := range output .Rules {
143+ r := map [string ]interface {}{
144+ "id" : aws .StringValue (rule .ID ),
145+ "enabled" : aws .StringValue (rule .Status ) == "Enabled" ,
146+ "expiration_days" : 0 ,
147+ "noncurrent_version_expiration_days" : 0 ,
148+ }
149+ // Check if the rule has a filter and set the prefix accordingly
150+ if rule .Filter != nil && rule .Filter .Prefix != nil {
151+ r ["prefix" ] = aws .StringValue (rule .Filter .Prefix )
152+ } else {
153+ r ["prefix" ] = ""
154+ }
155+ // Check if the rule has expiration or noncurrent version expiration days set
156+ if rule .Expiration != nil && rule .Expiration .Days != nil {
157+ r ["expiration_days" ] = int (* rule .Expiration .Days )
158+ }
159+ if rule .NoncurrentVersionExpiration != nil && rule .NoncurrentVersionExpiration .NoncurrentDays != nil {
160+ r ["noncurrent_version_expiration_days" ] = int (* rule .NoncurrentVersionExpiration .NoncurrentDays )
161+ }
162+ // Check if the rule has incomplete upload expiration days set
163+ if rule .AbortIncompleteMultipartUpload != nil && rule .AbortIncompleteMultipartUpload .DaysAfterInitiation != nil {
164+ r ["incomplete_upload_expiration_days" ] = int (* rule .AbortIncompleteMultipartUpload .DaysAfterInitiation )
165+ }
166+ rules = append (rules , r )
167+ }
168+ d .Set ("lifecycle_rule" , rules )
169+ }
170+
75171 return nil
76172}
77173
@@ -101,13 +197,143 @@ func resourceGridscaleBucketCreate(d *schema.ResourceData, meta interface{}) err
101197 return fmt .Errorf ("%s error: %v" , errorPrefix , err )
102198 }
103199
200+ lifecycleRules := d .Get ("lifecycle_rule" ).([]interface {})
201+ if len (lifecycleRules ) > 0 {
202+ lifecycleConfig := & s3.BucketLifecycleConfiguration {
203+ Rules : []* s3.LifecycleRule {},
204+ }
205+
206+ for _ , rule := range lifecycleRules {
207+ r := rule .(map [string ]interface {})
208+ lifecycleRule := & s3.LifecycleRule {
209+ ID : aws .String (r ["id" ].(string )),
210+ Filter : & s3.LifecycleRuleFilter {
211+ Prefix : aws .String (r ["prefix" ].(string )),
212+ },
213+ Status : aws .String ("Enabled" ),
214+ }
215+ // Check if the rule is enabled
216+ if ! r ["enabled" ].(bool ) {
217+ lifecycleRule .Status = aws .String ("Disabled" )
218+ }
219+ // Set expiration days if provided
220+ if v , ok := r ["expiration_days" ].(int ); ok && v > 0 {
221+ lifecycleRule .Expiration = & s3.LifecycleExpiration {
222+ Days : aws .Int64 (int64 (v )),
223+ }
224+ }
225+ // Set noncurrent version expiration days if provided
226+ if v , ok := r ["noncurrent_version_expiration_days" ].(int ); ok && v > 0 {
227+ lifecycleRule .NoncurrentVersionExpiration = & s3.NoncurrentVersionExpiration {
228+ NoncurrentDays : aws .Int64 (int64 (v )),
229+ }
230+ }
231+ // Set incomplete upload expiration days if provided
232+ if v , ok := r ["incomplete_upload_expiration_days" ].(int ); ok && v > 0 {
233+ lifecycleRule .AbortIncompleteMultipartUpload = & s3.AbortIncompleteMultipartUpload {
234+ DaysAfterInitiation : aws .Int64 (int64 (v )),
235+ }
236+ }
237+
238+ lifecycleConfig .Rules = append (lifecycleConfig .Rules , lifecycleRule )
239+ }
240+
241+ _ , err := s3Client .PutBucketLifecycleConfigurationWithContext (ctx , & s3.PutBucketLifecycleConfigurationInput {
242+ Bucket : & bucketNameStr ,
243+ LifecycleConfiguration : lifecycleConfig ,
244+ })
245+ if err != nil {
246+ // Delete the bucket if lifecycle configuration fails to set
247+ return resourceGridscaleBucketDelete (d , meta )
248+ }
249+ }
250+
104251 id := fmt .Sprintf ("%s/%s" , s3HostStr , bucketNameStr )
105252 d .SetId (id )
106253
107254 log .Printf ("The id for the new bucket has been set to %v" , id )
108255 return nil
109256}
110257
258+ func resourceGridscaleBucketUpdate (d * schema.ResourceData , meta interface {}) error {
259+ s3Host := d .Get ("s3_host" ).(string )
260+ accessKey := d .Get ("access_key" ).(string )
261+ secretKey := d .Get ("secret_key" ).(string )
262+ bucketName := d .Get ("bucket_name" ).(string )
263+
264+ s3Client := initS3Client (& gridscaleS3Provider {
265+ AccessKey : accessKey ,
266+ SecretKey : secretKey ,
267+ }, s3Host )
268+
269+ ctx , cancel := context .WithTimeout (context .Background (), d .Timeout (schema .TimeoutUpdate ))
270+ defer cancel ()
271+
272+ if d .HasChange ("lifecycle_rule" ) {
273+ lifecycleRules := d .Get ("lifecycle_rule" ).([]interface {})
274+
275+ if len (lifecycleRules ) == 0 {
276+ // If no lifecycle rules are provided, clear the lifecycle configuration
277+ _ , err := s3Client .DeleteBucketLifecycleWithContext (ctx , & s3.DeleteBucketLifecycleInput {
278+ Bucket : aws .String (bucketName ),
279+ })
280+ if err != nil {
281+ return fmt .Errorf ("error clearing lifecycle configuration for bucket %s using DeleteBucketLifecycle: %v" , bucketName , err )
282+ }
283+ return resourceGridscaleBucketRead (d , meta )
284+ } else {
285+ lifecycleConfig := & s3.BucketLifecycleConfiguration {
286+ Rules : []* s3.LifecycleRule {},
287+ }
288+
289+ for _ , rule := range lifecycleRules {
290+ r := rule .(map [string ]interface {})
291+ lifecycleRule := & s3.LifecycleRule {
292+ ID : aws .String (r ["id" ].(string )),
293+ Filter : & s3.LifecycleRuleFilter {
294+ Prefix : aws .String (r ["prefix" ].(string )),
295+ },
296+ Status : aws .String ("Enabled" ),
297+ }
298+
299+ if ! r ["enabled" ].(bool ) {
300+ lifecycleRule .Status = aws .String ("Disabled" )
301+ }
302+
303+ if v , ok := r ["expiration_days" ].(int ); ok && v > 0 {
304+ lifecycleRule .Expiration = & s3.LifecycleExpiration {
305+ Days : aws .Int64 (int64 (v )),
306+ }
307+ }
308+
309+ if v , ok := r ["noncurrent_version_expiration_days" ].(int ); ok && v > 0 {
310+ lifecycleRule .NoncurrentVersionExpiration = & s3.NoncurrentVersionExpiration {
311+ NoncurrentDays : aws .Int64 (int64 (v )),
312+ }
313+ }
314+
315+ if v , ok := r ["incomplete_upload_expiration_days" ].(int ); ok && v > 0 {
316+ lifecycleRule .AbortIncompleteMultipartUpload = & s3.AbortIncompleteMultipartUpload {
317+ DaysAfterInitiation : aws .Int64 (int64 (v )),
318+ }
319+ }
320+
321+ lifecycleConfig .Rules = append (lifecycleConfig .Rules , lifecycleRule )
322+ }
323+
324+ _ , err := s3Client .PutBucketLifecycleConfigurationWithContext (ctx , & s3.PutBucketLifecycleConfigurationInput {
325+ Bucket : aws .String (bucketName ),
326+ LifecycleConfiguration : lifecycleConfig ,
327+ })
328+ if err != nil {
329+ return fmt .Errorf ("error updating lifecycle configuration for bucket %s: %v" , bucketName , err )
330+ }
331+ }
332+ }
333+
334+ return resourceGridscaleBucketRead (d , meta )
335+ }
336+
111337func resourceGridscaleBucketDelete (d * schema.ResourceData , meta interface {}) error {
112338 s3Host := d .Get ("s3_host" )
113339 accessKey := d .Get ("access_key" )
0 commit comments