@@ -298,30 +298,67 @@ func (r *SnapmirrorPolicyResource) Read(ctx context.Context, req resource.ReadRe
298298 data .CreateSnapshotOnSource = types .BoolValue (restInfo .CreateSnapshotOnSource )
299299 data .ID = types .StringValue (restInfo .UUID )
300300
301- // if len(restInfo.Retention) == 0 {
302- if restInfo .Retention == nil {
303- data .Retention = nil
304- } else {
305- data .Retention = []RetentionModel {}
306- for _ , item := range restInfo .Retention {
307- var retention RetentionModel
308- // conver count from string to int
309- count , err := strconv .Atoi (item .Count )
310- if err != nil {
311- errorHandler .MakeAndReportError ("Decode count error" , "snapmirror_policy retention count is not valid" )
312- return
313- }
314- retention .Count = types .Int64Value (int64 (count ))
315- if item .CreationSchedule .Name != "" {
316- retention .CreationScheduleName = types .StringValue (item .CreationSchedule .Name )
317- }
318- if item .Label != "" {
319- retention .Label = types .StringValue (item .Label )
301+ // preserve a null/empty retention in state unless the user configured rules
302+ if len (data .Retention ) > 0 {
303+ if restInfo .Retention == nil {
304+ data .Retention = nil
305+ } else {
306+ // Match by label and preserve the configured order. This also avoids
307+ // persisting any additional default rule(s) returned by the API.
308+ restRetentionByLabel := make (map [string ]interfaces.RetentionGetRawDataModel , len (restInfo .Retention ))
309+ for _ , item := range restInfo .Retention {
310+ if item .Label == "" {
311+ continue
312+ }
313+ // don't persist default retention rule returned by ONTAP
314+ if item .Label == "sm_created" && item .Count == "1" {
315+ continue
316+ }
317+ restRetentionByLabel [item .Label ] = item
320318 }
321- if item .Prefix != "" {
322- retention .Prefix = types .StringValue (item .Prefix )
319+
320+ configuredRetention := data .Retention
321+ data .Retention = make ([]RetentionModel , 0 , len (configuredRetention ))
322+ for _ , configured := range configuredRetention {
323+ retention := configured
324+ // store unknown optional values as unset in state
325+ if retention .Count .IsUnknown () {
326+ retention .Count = types .Int64Null ()
327+ }
328+ if retention .CreationScheduleName .IsUnknown () {
329+ retention .CreationScheduleName = types .StringNull ()
330+ }
331+ if retention .Prefix .IsUnknown () {
332+ retention .Prefix = types .StringNull ()
333+ }
334+
335+ // drop the default rule if it already exists in prior state
336+ if retention .Label .ValueString () == "sm_created" && (retention .Count .IsNull () || retention .Count .ValueInt64 () == 1 ) {
337+ continue
338+ }
339+ configuredLabel := retention .Label .ValueString ()
340+ restItem , ok := restRetentionByLabel [configuredLabel ]
341+ if ! ok {
342+ data .Retention = append (data .Retention , retention )
343+ continue
344+ }
345+ retention .Label = types .StringValue (restItem .Label )
346+ if ! retention .Count .IsNull () {
347+ count , err := strconv .Atoi (restItem .Count )
348+ if err != nil {
349+ errorHandler .MakeAndReportError ("Decode count error" , "snapmirror_policy retention count is not valid" )
350+ return
351+ }
352+ retention .Count = types .Int64Value (int64 (count ))
353+ }
354+ if ! retention .CreationScheduleName .IsNull () && ! retention .CreationScheduleName .IsUnknown () && restItem .CreationSchedule .Name != "" {
355+ retention .CreationScheduleName = types .StringValue (restItem .CreationSchedule .Name )
356+ }
357+ if ! retention .Prefix .IsNull () && ! retention .Prefix .IsUnknown () && restItem .Prefix != "" {
358+ retention .Prefix = types .StringValue (restItem .Prefix )
359+ }
360+ data .Retention = append (data .Retention , retention )
323361 }
324- data .Retention = append (data .Retention , retention )
325362 }
326363 }
327364
@@ -415,30 +452,68 @@ func (r *SnapmirrorPolicyResource) Create(ctx context.Context, req resource.Crea
415452 tflog .Debug (ctx , fmt .Sprintf ("create snapmirror policy get resource: %#v" , resource ))
416453 // Update the computed parameters
417454 data .ID = types .StringValue (resource .UUID )
418- if resource .Retention == nil {
419- data .Retention = nil
420- tflog .Debug (ctx , fmt .Sprintf ("create snapmirror policy retention is nil: %#v" , data .Retention ))
421- } else {
422- data .Retention = []RetentionModel {}
423- for _ , item := range resource .Retention {
424- var retention RetentionModel
425- // conver count from string to int
426- count , err := strconv .Atoi (item .Count )
427- if err != nil {
428- errorHandler .MakeAndReportError ("decode count error" , "snapmirror_policy retention count is not valid" )
429- return
430- }
431- retention .Count = types .Int64Value (int64 (count ))
432- if item .CreationSchedule .Name != "" {
433- retention .CreationScheduleName = types .StringValue (item .CreationSchedule .Name )
434- }
435- if item .Label != "" {
436- retention .Label = types .StringValue (item .Label )
455+ // preserve null/empty retention when it was not configured
456+ if len (data .Retention ) > 0 {
457+ if resource .Retention == nil {
458+ data .Retention = nil
459+ tflog .Debug (ctx , fmt .Sprintf ("create snapmirror policy retention is nil: %#v" , data .Retention ))
460+ } else {
461+ // Match by label and preserve the configured order to avoid including
462+ // any additional default rule(s) returned by the API.
463+ restRetentionByLabel := make (map [string ]interfaces.RetentionGetRawDataModel , len (resource .Retention ))
464+ for _ , item := range resource .Retention {
465+ if item .Label == "" {
466+ continue
467+ }
468+ // don't persist default retention rule returned by ONTAP
469+ if item .Label == "sm_created" && item .Count == "1" {
470+ continue
471+ }
472+ restRetentionByLabel [item .Label ] = item
437473 }
438- if item .Prefix != "" {
439- retention .Prefix = types .StringValue (item .Prefix )
474+
475+ configuredRetention := data .Retention
476+ data .Retention = make ([]RetentionModel , 0 , len (configuredRetention ))
477+ for _ , configured := range configuredRetention {
478+ retention := configured
479+ // store unknown optional values as unset in state
480+ if retention .Count .IsUnknown () {
481+ retention .Count = types .Int64Null ()
482+ }
483+ if retention .CreationScheduleName .IsUnknown () {
484+ retention .CreationScheduleName = types .StringNull ()
485+ }
486+ if retention .Prefix .IsUnknown () {
487+ retention .Prefix = types .StringNull ()
488+ }
489+
490+ // drop the default rule if it already exists in prior state
491+ if retention .Label .ValueString () == "sm_created" && (retention .Count .IsNull () || retention .Count .ValueInt64 () == 1 ) {
492+ continue
493+ }
494+ configuredLabel := retention .Label .ValueString ()
495+ restItem , ok := restRetentionByLabel [configuredLabel ]
496+ if ! ok {
497+ data .Retention = append (data .Retention , retention )
498+ continue
499+ }
500+ retention .Label = types .StringValue (restItem .Label )
501+ if ! retention .Count .IsNull () {
502+ count , err := strconv .Atoi (restItem .Count )
503+ if err != nil {
504+ errorHandler .MakeAndReportError ("decode count error" , "snapmirror_policy retention count is not valid" )
505+ return
506+ }
507+ retention .Count = types .Int64Value (int64 (count ))
508+ }
509+ if ! retention .CreationScheduleName .IsNull () && ! retention .CreationScheduleName .IsUnknown () && restItem .CreationSchedule .Name != "" {
510+ retention .CreationScheduleName = types .StringValue (restItem .CreationSchedule .Name )
511+ }
512+ if ! retention .Prefix .IsNull () && ! retention .Prefix .IsUnknown () && restItem .Prefix != "" {
513+ retention .Prefix = types .StringValue (restItem .Prefix )
514+ }
515+ data .Retention = append (data .Retention , retention )
440516 }
441- data .Retention = append (data .Retention , retention )
442517 }
443518 }
444519 data .Type = types .StringValue (resource .Type )
@@ -582,29 +657,67 @@ func (r *SnapmirrorPolicyResource) Update(ctx context.Context, req resource.Upda
582657 return
583658 }
584659
585- if restInfo .Retention == nil {
586- plan .Retention = nil
587- } else {
588- plan .Retention = []RetentionModel {}
589- for _ , item := range restInfo .Retention {
590- var retention RetentionModel
591- // conver count from string to int
592- count , err := strconv .Atoi (item .Count )
593- if err != nil {
594- errorHandler .MakeAndReportError ("decode count error" , "snapmirror_policy retention count is not valid" )
595- return
596- }
597- retention .Count = types .Int64Value (int64 (count ))
598- if item .CreationSchedule .Name != "" {
599- retention .CreationScheduleName = types .StringValue (item .CreationSchedule .Name )
600- }
601- if item .Label != "" {
602- retention .Label = types .StringValue (item .Label )
660+ // preserve null/empty retention when it was not configured
661+ if len (plan .Retention ) > 0 {
662+ if restInfo .Retention == nil {
663+ plan .Retention = nil
664+ } else {
665+ // Match by label and preserve the configured order to avoid including
666+ // any additional default rule(s) returned by the API.
667+ restRetentionByLabel := make (map [string ]interfaces.RetentionGetRawDataModel , len (restInfo .Retention ))
668+ for _ , item := range restInfo .Retention {
669+ if item .Label == "" {
670+ continue
671+ }
672+ // don't persist default retention rule returned by ONTAP
673+ if item .Label == "sm_created" && item .Count == "1" {
674+ continue
675+ }
676+ restRetentionByLabel [item .Label ] = item
603677 }
604- if item .Prefix != "" {
605- retention .Prefix = types .StringValue (item .Prefix )
678+
679+ configuredRetention := plan .Retention
680+ plan .Retention = make ([]RetentionModel , 0 , len (configuredRetention ))
681+ for _ , configured := range configuredRetention {
682+ retention := configured
683+ // store unknown optional values as unset in state
684+ if retention .Count .IsUnknown () {
685+ retention .Count = types .Int64Null ()
686+ }
687+ if retention .CreationScheduleName .IsUnknown () {
688+ retention .CreationScheduleName = types .StringNull ()
689+ }
690+ if retention .Prefix .IsUnknown () {
691+ retention .Prefix = types .StringNull ()
692+ }
693+
694+ // drop the default rule if it already exists in prior plan/state
695+ if retention .Label .ValueString () == "sm_created" && (retention .Count .IsNull () || retention .Count .ValueInt64 () == 1 ) {
696+ continue
697+ }
698+ configuredLabel := retention .Label .ValueString ()
699+ restItem , ok := restRetentionByLabel [configuredLabel ]
700+ if ! ok {
701+ plan .Retention = append (plan .Retention , retention )
702+ continue
703+ }
704+ retention .Label = types .StringValue (restItem .Label )
705+ if ! retention .Count .IsNull () {
706+ count , err := strconv .Atoi (restItem .Count )
707+ if err != nil {
708+ errorHandler .MakeAndReportError ("decode count error" , "snapmirror_policy retention count is not valid" )
709+ return
710+ }
711+ retention .Count = types .Int64Value (int64 (count ))
712+ }
713+ if ! retention .CreationScheduleName .IsNull () && ! retention .CreationScheduleName .IsUnknown () && restItem .CreationSchedule .Name != "" {
714+ retention .CreationScheduleName = types .StringValue (restItem .CreationSchedule .Name )
715+ }
716+ if ! retention .Prefix .IsNull () && ! retention .Prefix .IsUnknown () && restItem .Prefix != "" {
717+ retention .Prefix = types .StringValue (restItem .Prefix )
718+ }
719+ plan .Retention = append (plan .Retention , retention )
606720 }
607- plan .Retention = append (plan .Retention , retention )
608721 }
609722 }
610723
0 commit comments