@@ -324,7 +324,7 @@ func parseISO8601(s string) time.Time {
324324 return t
325325}
326326
327- func expandRules (ctx context.Context , in []LifecycleRuleModel ) []s3types.LifecycleRule {
327+ func expandRules (ctx context.Context , in []LifecycleRuleModel ) []s3types.LifecycleRule { //nolint:gocyclo
328328 out := make ([]s3types.LifecycleRule , 0 , len (in ))
329329 for _ , r := range in {
330330 rule := s3types.LifecycleRule {
@@ -367,6 +367,16 @@ func expandRules(ctx context.Context, in []LifecycleRuleModel) []s3types.Lifecyc
367367 }
368368 rule .NoncurrentVersionExpiration = & nc
369369 }
370+ if r .NoncurrentVersionTransitions != nil {
371+ for _ , noncurrentTransition := range r .NoncurrentVersionTransitions {
372+ nct := s3types.NoncurrentVersionTransition {
373+ StorageClass : s3types .TransitionStorageClass (noncurrentTransition .StorageClass .ValueString ()),
374+ NoncurrentDays : noncurrentTransition .NoncurrentDays .ValueInt32Pointer (),
375+ NewerNoncurrentVersions : noncurrentTransition .NewerNoncurrentVersions .ValueInt32Pointer (),
376+ }
377+ rule .NoncurrentVersionTransitions = append (rule .NoncurrentVersionTransitions , nct )
378+ }
379+ }
370380 if r .AbortIncompleteMultipart != nil {
371381 ai := s3types.AbortIncompleteMultipartUpload {
372382 DaysAfterInitiation : aws .Int32 (r .AbortIncompleteMultipart .DaysAfterInitiation .ValueInt32 ()),
@@ -559,24 +569,27 @@ func eqLifecycleRule(a, b s3types.LifecycleRule) bool { //nolint:gocyclo
559569 return true
560570}
561571
562- func waitForLifecycleConfig (parentCtx context.Context , client * s3.Client , bucket string , expected s3types.BucketLifecycleConfiguration ) error {
572+ func waitForLifecycleConfig (parentCtx context.Context , client * s3.Client , bucket string , expected s3types.BucketLifecycleConfiguration ) ( * s3. GetBucketLifecycleConfigurationOutput , error ) {
563573 // make a sorted copy of expected rules
564- exp := append ([]s3types.LifecycleRule (nil ), expected .Rules ... )
565- slices .SortFunc (exp , cmpLifecycleRule )
574+ exp := slices .SortedFunc (slices .Values (expected .Rules ), cmpLifecycleRule )
566575
567- return coreweave .PollUntil ("bucket lifecycle configuration" , parentCtx , 5 * time .Second , 5 * time .Minute , func (ctx context.Context ) (bool , error ) {
568- out , err := client .GetBucketLifecycleConfiguration (ctx , & s3.GetBucketLifecycleConfigurationInput {Bucket : aws .String (bucket )})
576+ var out * s3.GetBucketLifecycleConfigurationOutput
577+ err := coreweave .PollUntil ("bucket lifecycle configuration" , parentCtx , 5 * time .Second , 5 * time .Minute , func (ctx context.Context ) (bool , error ) {
578+ result , err := client .GetBucketLifecycleConfiguration (ctx , & s3.GetBucketLifecycleConfigurationInput {Bucket : aws .String (bucket )})
569579 if err != nil {
580+ out = nil
570581 if isTransientS3Error (err ) {
571582 return false , nil
572583 }
573584 return false , err
574585 }
586+ out = result
575587
576- rules := out . Rules
577- slices .SortFunc ( rules , cmpLifecycleRule )
588+ // Make sorted a copy of the slice to sort for comparison, to avoid mutating the returned slice by reference.
589+ rules := slices .SortedFunc ( slices . Values ( result . Rules ) , cmpLifecycleRule )
578590 return slices .EqualFunc (exp , rules , eqLifecycleRule ), nil
579591 })
592+ return out , err
580593}
581594
582595func (r * BucketLifecycleResource ) Create (ctx context.Context , req resource.CreateRequest , resp * resource.CreateResponse ) {
@@ -618,9 +631,11 @@ func (r *BucketLifecycleResource) Create(ctx context.Context, req resource.Creat
618631 }
619632
620633 // wait for lifecycle config to be read back from s3 API since it is not guaranteed to propagate immediately
621- if err := waitForLifecycleConfig (ctx , s3c , data .Bucket .ValueString (), * lifecycleConfig ); err != nil {
634+ if result , err := waitForLifecycleConfig (ctx , s3c , data .Bucket .ValueString (), * lifecycleConfig ); err != nil {
622635 handleS3Error (err , & resp .Diagnostics , data .Bucket .ValueString ())
623636 return
637+ } else {
638+ data .Rule = flattenLifecycleRules (result .Rules )
624639 }
625640
626641 resp .Diagnostics .Append (resp .State .Set (ctx , & data )... )
@@ -787,9 +802,12 @@ func (r *BucketLifecycleResource) Update(ctx context.Context, req resource.Updat
787802 }
788803
789804 // wait for lifecycle config to be read back from s3 API since it is not guaranteed to propagate immediately
790- if err := waitForLifecycleConfig (ctx , s3c , data .Bucket .ValueString (), * lifecycleConfig ); err != nil {
805+ if result , err := waitForLifecycleConfig (ctx , s3c , data .Bucket .ValueString (), * lifecycleConfig ); err != nil {
791806 handleS3Error (err , & resp .Diagnostics , data .Bucket .ValueString ())
792807 return
808+ } else {
809+ // Read the result back into state. Terraform will detect and fail if the state does not match the plan.
810+ data .Rule = flattenLifecycleRules (result .Rules )
793811 }
794812
795813 resp .Diagnostics .Append (resp .State .Set (ctx , & data )... )
0 commit comments