Skip to content

Commit 6b9ef79

Browse files
committed
fixing bugs
1 parent ceef8f4 commit 6b9ef79

File tree

3 files changed

+129
-48
lines changed

3 files changed

+129
-48
lines changed

octopusdeploy_framework/resource_lifecycle.go

Lines changed: 112 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,14 @@ func (r *lifecycleTypeResource) Create(ctx context.Context, req resource.CreateR
7171
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
7272
validateRetentionBlocksUsed(data, &resp.Diagnostics, r.serverSupportsRetentionWithStrategy)
7373
isRetentionWithStrategyUsedExclusively := retentionWithStrategyWillBeUsedExclusively(data, r.serverSupportsRetentionWithStrategy)
74-
7574
if resp.Diagnostics.HasError() {
7675
return
7776
}
7877

7978
hasUserDefinedReleaseRetention, hasUserDefinedTentacleRetention := setDefaultRetention(data, isRetentionWithStrategyUsedExclusively)
8079
hasUserDefinedReleaseRetentionWithStrategy, hasUserDefinedTentacleRetentionWithStrategy := setDefaultRetentionWithStrategy(data, isRetentionWithStrategyUsedExclusively)
8180

82-
newLifecycle := expandLifecycle(data)
81+
newLifecycle := expandLifecycle(data, isRetentionWithStrategyUsedExclusively)
8382
lifecycle, err := lifecycles.Add(r.Config.Client, newLifecycle)
8483
if err != nil {
8584
resp.Diagnostics.AddError("unable to create lifecycle", err.Error())
@@ -89,9 +88,10 @@ func (r *lifecycleTypeResource) Create(ctx context.Context, req resource.CreateR
8988
handleUnitCasing(lifecycle, newLifecycle)
9089

9190
data = flattenLifecycleResource(lifecycle, isRetentionWithStrategyUsedExclusively)
91+
tflog.Debug(ctx, fmt.Sprintf("after the flatten rose '%v'", data.Phase))
9292
removeDefaultRetentionFromUnsetBlocks(data, hasUserDefinedReleaseRetention, hasUserDefinedTentacleRetention)
9393
removeDefaultRetentionWithStrategyFromUnsetBlocks(data, hasUserDefinedReleaseRetentionWithStrategy, hasUserDefinedTentacleRetentionWithStrategy)
94-
94+
tflog.Debug(ctx, fmt.Sprintf("after the removal rose '%v'", data.Phase))
9595
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
9696
}
9797

@@ -113,7 +113,7 @@ func (r *lifecycleTypeResource) Read(ctx context.Context, req resource.ReadReque
113113
return
114114
}
115115

116-
handleUnitCasing(lifecycle, expandLifecycle(data))
116+
handleUnitCasing(lifecycle, expandLifecycle(data, isRetentionWithStrategyUsedExclusively))
117117

118118
data = flattenLifecycleResource(lifecycle, isRetentionWithStrategyUsedExclusively)
119119

@@ -131,14 +131,15 @@ func (r *lifecycleTypeResource) Update(ctx context.Context, req resource.UpdateR
131131
if resp.Diagnostics.HasError() {
132132
return
133133
}
134+
134135
validateRetentionBlocksUsed(data, &resp.Diagnostics, r.serverSupportsRetentionWithStrategy)
135136
isRetentionWithStrategyUsedExclusively := retentionWithStrategyWillBeUsedExclusively(data, r.serverSupportsRetentionWithStrategy)
136137
hasUserDefinedReleaseRetention, hasUserDefinedTentacleRetention := setDefaultRetention(data, isRetentionWithStrategyUsedExclusively)
137138
hasUserDefinedReleaseRetentionWithStrategy, hasUserDefinedTentacleRetentionWithStrategy := setDefaultRetentionWithStrategy(data, isRetentionWithStrategyUsedExclusively)
138139

139140
tflog.Debug(ctx, fmt.Sprintf("updating lifecycle '%s'", data.ID.ValueString()))
140141

141-
lifecycle := expandLifecycle(data)
142+
lifecycle := expandLifecycle(data, isRetentionWithStrategyUsedExclusively)
142143
lifecycle.ID = state.ID.ValueString()
143144

144145
updatedLifecycle, err := lifecycles.Update(r.Config.Client, lifecycle)
@@ -350,7 +351,7 @@ func flattenLifecycleResource(lifecycle *lifecycles.Lifecycle, isRetentionWithSt
350351
SpaceID: types.StringValue(lifecycle.SpaceID),
351352
Name: types.StringValue(lifecycle.Name),
352353
Description: types.StringValue(lifecycle.Description),
353-
Phase: flattenPhases(lifecycle.Phases),
354+
Phase: flattenPhasesWithStrategy(lifecycle.Phases),
354355
ReleaseRetentionWithStrategy: flattenRetentionWithStrategy(lifecycle.ReleaseRetentionPolicy),
355356
TentacleRetentionWithStrategy: flattenRetentionWithStrategy(lifecycle.TentacleRetentionPolicy),
356357
}
@@ -371,8 +372,35 @@ func flattenLifecycleResource(lifecycle *lifecycles.Lifecycle, isRetentionWithSt
371372
}
372373

373374
func flattenPhases(phases []*lifecycles.Phase) types.List {
375+
var attributeTypes = getPhaseAttrTypes()
376+
if phases == nil {
377+
return types.ListNull(types.ObjectType{AttrTypes: attributeTypes})
378+
}
379+
phasesList := make([]attr.Value, 0, len(phases))
380+
381+
for _, phase := range phases {
382+
attrs := map[string]attr.Value{
383+
"id": types.StringValue(phase.ID),
384+
"name": types.StringValue(phase.Name),
385+
"automatic_deployment_targets": util.FlattenStringList(phase.AutomaticDeploymentTargets),
386+
"optional_deployment_targets": util.FlattenStringList(phase.OptionalDeploymentTargets),
387+
"minimum_environments_before_promotion": types.Int64Value(int64(phase.MinimumEnvironmentsBeforePromotion)),
388+
"is_optional_phase": types.BoolValue(phase.IsOptionalPhase),
389+
"is_priority_phase": types.BoolValue(phase.IsPriorityPhase),
390+
"release_retention_policy": util.Ternary(phase.ReleaseRetentionPolicy != nil, flattenRetention(phase.ReleaseRetentionPolicy), types.ListNull(types.ObjectType{AttrTypes: getRetentionAttrTypes()})),
391+
"tentacle_retention_policy": util.Ternary(phase.TentacleRetentionPolicy != nil, flattenRetention(phase.TentacleRetentionPolicy), types.ListNull(types.ObjectType{AttrTypes: getRetentionAttrTypes()})),
392+
"release_retention_with_strategy": types.ListNull(types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()}),
393+
"tentacle_retention_with_strategy": types.ListNull(types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()}),
394+
}
395+
phasesList = append(phasesList, types.ObjectValueMust(attributeTypes, attrs))
396+
}
397+
return types.ListValueMust(types.ObjectType{AttrTypes: attributeTypes}, phasesList)
398+
}
399+
400+
func flattenPhasesWithStrategy(phases []*lifecycles.Phase) types.List {
401+
var attributeTypes = getPhaseWithStrategyAttrTypes()
374402
if phases == nil {
375-
return types.ListNull(types.ObjectType{AttrTypes: getPhaseAttrTypes()})
403+
return types.ListNull(types.ObjectType{AttrTypes: attributeTypes})
376404
}
377405
phasesList := make([]attr.Value, 0, len(phases))
378406

@@ -385,14 +413,14 @@ func flattenPhases(phases []*lifecycles.Phase) types.List {
385413
"minimum_environments_before_promotion": types.Int64Value(int64(phase.MinimumEnvironmentsBeforePromotion)),
386414
"is_optional_phase": types.BoolValue(phase.IsOptionalPhase),
387415
"is_priority_phase": types.BoolValue(phase.IsPriorityPhase),
388-
"release_retention": util.Ternary(phase.ReleaseRetentionPolicy != nil, flattenRetention(phase.ReleaseRetentionPolicy), types.ListNull(types.ObjectType{AttrTypes: getRetentionAttrTypes()})),
389-
"tentacle_retention": util.Ternary(phase.TentacleRetentionPolicy != nil, flattenRetention(phase.TentacleRetentionPolicy), types.ListNull(types.ObjectType{AttrTypes: getRetentionAttrTypes()})),
416+
"release_retention_policy": types.ListNull(types.ObjectType{AttrTypes: getRetentionAttrTypes()}),
417+
"tentacle_retention_policy": types.ListNull(types.ObjectType{AttrTypes: getRetentionAttrTypes()}),
390418
"release_retention_with_strategy": util.Ternary(phase.ReleaseRetentionPolicy != nil, flattenRetentionWithStrategy(phase.ReleaseRetentionPolicy), types.ListNull(types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()})),
391419
"tentacle_retention_with_strategy": util.Ternary(phase.TentacleRetentionPolicy != nil, flattenRetentionWithStrategy(phase.TentacleRetentionPolicy), types.ListNull(types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()})),
392420
}
393-
phasesList = append(phasesList, types.ObjectValueMust(getPhaseAttrTypes(), attrs))
421+
phasesList = append(phasesList, types.ObjectValueMust(attributeTypes, attrs))
394422
}
395-
return types.ListValueMust(types.ObjectType{AttrTypes: getPhaseAttrTypes()}, phasesList)
423+
return types.ListValueMust(types.ObjectType{AttrTypes: attributeTypes}, phasesList)
396424
}
397425

398426
func flattenRetention(retention *core.RetentionPeriod) types.List {
@@ -435,7 +463,7 @@ func flattenRetentionWithStrategy(retentionWithStrategy *core.RetentionPeriod) t
435463
)
436464
}
437465

438-
func expandLifecycle(data *lifecycleTypeResourceModel) *lifecycles.Lifecycle {
466+
func expandLifecycle(data *lifecycleTypeResourceModel, isRetentionWithStrategyUsedExclusively bool) *lifecycles.Lifecycle {
439467
if data == nil {
440468
return nil
441469
}
@@ -447,15 +475,17 @@ func expandLifecycle(data *lifecycleTypeResourceModel) *lifecycles.Lifecycle {
447475
lifecycle.ID = data.ID.ValueString()
448476
}
449477

450-
lifecycle.Phases = expandPhases(data.Phase)
451-
lifecycle.ReleaseRetentionPolicy = expandRetentionWithOrWithoutStrategy(data.ReleaseRetention, data.ReleaseRetentionWithStrategy)
452-
lifecycle.TentacleRetentionPolicy = expandRetentionWithOrWithoutStrategy(data.TentacleRetention, data.TentacleRetentionWithStrategy)
453-
478+
if isRetentionWithStrategyUsedExclusively {
479+
lifecycle.Phases = expandPhasesWithStrategy(data.Phase)
480+
lifecycle.ReleaseRetentionPolicy = expandRetentionWithStrategy(data.ReleaseRetentionWithStrategy)
481+
} else {
482+
lifecycle.Phases = expandPhases(data.Phase)
483+
lifecycle.ReleaseRetentionPolicy = expandRetention(data.ReleaseRetention)
484+
}
454485
return lifecycle
455486
}
456487

457488
func expandPhases(phases types.List) []*lifecycles.Phase {
458-
//TODO: don't allow with strategy to be used with the pre 2025.3 octopus
459489
if phases.IsNull() || phases.IsUnknown() || len(phases.Elements()) == 0 {
460490
return nil
461491
}
@@ -499,32 +529,67 @@ func expandPhases(phases types.List) []*lifecycles.Phase {
499529
if v, ok := phaseAttrs["release_retention_policy"].(types.List); ok && !v.IsNull() {
500530
phase.ReleaseRetentionPolicy = expandRetention(v)
501531
}
502-
if v, ok := phaseAttrs["release_retention_with_strategy"].(types.List); ok && !v.IsNull() {
503-
phase.ReleaseRetentionPolicy = expandRetentionWithStrategy(v)
504-
}
505532
if v, ok := phaseAttrs["tentacle_retention_policy"].(types.List); ok && !v.IsNull() {
506533
phase.TentacleRetentionPolicy = expandRetention(v)
507534
}
508-
if v, ok := phaseAttrs["release_retention_with_strategy"].(types.List); ok && !v.IsNull() {
509-
phase.TentacleRetentionPolicy = expandRetentionWithStrategy(v)
510-
}
511535
result = append(result, phase)
512536
}
513537

514538
return result
515539
}
516-
517-
func expandRetentionWithOrWithoutStrategy(oldRetention types.List, newRetention types.List) *core.RetentionPeriod {
518-
oldRetentionPresent := !oldRetention.IsNull() && !oldRetention.IsUnknown()
519-
newRetentionPresent := !newRetention.IsNull() && !newRetention.IsUnknown()
520-
if !oldRetentionPresent && !newRetentionPresent {
540+
func expandPhasesWithStrategy(phases types.List) []*lifecycles.Phase {
541+
if phases.IsNull() || phases.IsUnknown() || len(phases.Elements()) == 0 {
521542
return nil
522543
}
523-
if oldRetentionPresent {
524-
return expandRetention(oldRetention)
544+
545+
result := make([]*lifecycles.Phase, 0, len(phases.Elements()))
546+
547+
for _, phaseElem := range phases.Elements() {
548+
phaseObj := phaseElem.(types.Object)
549+
phaseAttrs := phaseObj.Attributes()
550+
551+
phase := &lifecycles.Phase{}
552+
553+
if v, ok := phaseAttrs["id"].(types.String); ok && !v.IsNull() {
554+
phase.ID = v.ValueString()
555+
}
556+
557+
if v, ok := phaseAttrs["name"].(types.String); ok && !v.IsNull() {
558+
phase.Name = v.ValueString()
559+
}
560+
561+
if v, ok := phaseAttrs["automatic_deployment_targets"].(types.List); ok && !v.IsNull() {
562+
phase.AutomaticDeploymentTargets = util.ExpandStringList(v)
563+
}
564+
565+
if v, ok := phaseAttrs["optional_deployment_targets"].(types.List); ok && !v.IsNull() {
566+
phase.OptionalDeploymentTargets = util.ExpandStringList(v)
567+
}
568+
569+
if v, ok := phaseAttrs["minimum_environments_before_promotion"].(types.Int64); ok && !v.IsNull() {
570+
phase.MinimumEnvironmentsBeforePromotion = int32(v.ValueInt64())
571+
}
572+
573+
if v, ok := phaseAttrs["is_optional_phase"].(types.Bool); ok && !v.IsNull() {
574+
phase.IsOptionalPhase = v.ValueBool()
575+
}
576+
577+
if v, ok := phaseAttrs["is_priority_phase"].(types.Bool); ok && !v.IsNull() {
578+
phase.IsPriorityPhase = v.ValueBool()
579+
}
580+
581+
if v, ok := phaseAttrs["release_retention_with_strategy"].(types.List); ok && !v.IsNull() {
582+
phase.ReleaseRetentionPolicy = expandRetentionWithStrategy(v)
583+
}
584+
if v, ok := phaseAttrs["release_retention_with_strategy"].(types.List); ok && !v.IsNull() {
585+
phase.TentacleRetentionPolicy = expandRetentionWithStrategy(v)
586+
}
587+
result = append(result, phase)
525588
}
526-
return expandRetentionWithStrategy(newRetention)
589+
590+
return result
527591
}
592+
528593
func expandRetention(v types.List) *core.RetentionPeriod {
529594
if v.IsNull() || v.IsUnknown() || len(v.Elements()) == 0 {
530595
return nil
@@ -615,3 +680,19 @@ func getPhaseAttrTypes() map[string]attr.Type {
615680
"tentacle_retention_with_strategy": types.ListType{ElemType: types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()}},
616681
}
617682
}
683+
684+
func getPhaseWithStrategyAttrTypes() map[string]attr.Type {
685+
return map[string]attr.Type{
686+
"id": types.StringType,
687+
"name": types.StringType,
688+
"automatic_deployment_targets": types.ListType{ElemType: types.StringType},
689+
"optional_deployment_targets": types.ListType{ElemType: types.StringType},
690+
"minimum_environments_before_promotion": types.Int64Type,
691+
"is_optional_phase": types.BoolType,
692+
"is_priority_phase": types.BoolType,
693+
"release_retention_policy": types.ListType{ElemType: types.ObjectType{AttrTypes: getRetentionAttrTypes()}},
694+
"tentacle_retention_policy": types.ListType{ElemType: types.ObjectType{AttrTypes: getRetentionAttrTypes()}},
695+
"release_retention_with_strategy": types.ListType{ElemType: types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()}},
696+
"tentacle_retention_with_strategy": types.ListType{ElemType: types.ObjectType{AttrTypes: getRetentionWithStrategyAttrTypes()}},
697+
}
698+
}

octopusdeploy_framework/resource_lifecycle_retention_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func TestAccLifecycleRetentionPolicyUpdates(t *testing.T) {
8686
// 4 update with Default retention policies
8787
{
8888
Config: defaultRetentionLifecycle_notUsingQuantityToKeep(lifecycleName),
89-
Check: resource.ComposeTestCheckFunc(
89+
Check: resource.ComposeTestCheckFunc(3
9090
testAccCheckLifecycleExists(lifecycleResource),
9191
resource.TestCheckResourceAttrSet(lifecycleResource, "id"),
9292
resource.TestCheckResourceAttr(lifecycleResource, "name", lifecycleName),

octopusdeploy_framework/schemas/lifecycle.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -136,19 +136,6 @@ func getLifecyclesAttribute() datasourceSchema.ListNestedAttribute {
136136
}
137137
}
138138

139-
func getRetentionWithStrategyAttribute() datasourceSchema.Attribute {
140-
return datasourceSchema.ListNestedAttribute{
141-
Computed: true,
142-
NestedObject: datasourceSchema.NestedAttributeObject{
143-
Attributes: map[string]datasourceSchema.Attribute{
144-
"strategy": util.DataSourceString().Computed().Description("The retention strategy.").Build(),
145-
"quantity_to_keep": util.DataSourceInt64().Computed().Description("The quantity of releases to keep.").Build(),
146-
"unit": util.DataSourceString().Computed().Description("The unit of time for the retention policy.").Build(),
147-
},
148-
},
149-
}
150-
}
151-
152139
func getPhasesAttribute() datasourceSchema.ListNestedAttribute {
153140
return datasourceSchema.ListNestedAttribute{
154141
Computed: true,
@@ -176,7 +163,7 @@ func getResourceRetentionWithStrategyBlockSchema() resourceSchema.ListNestedBloc
176163
NestedObject: resourceSchema.NestedBlockObject{
177164
Attributes: map[string]resourceSchema.Attribute{
178165
"strategy": util.ResourceString().
179-
Required().
166+
Optional().Computed().
180167
Validators(stringvalidator.OneOf(core.RetentionStrategyDefault, core.RetentionStrategyCount, core.RetentionStrategyForever)).
181168
Description("How retention will be set. Valid strategies are `Default`, `Forever`, and `Count`. The default value is `Default`." +
182169
"\n - `strategy = \"Default\"`, is used if the retention is set by the space-wide default lifecycle retention policy. " +
@@ -191,7 +178,7 @@ func getResourceRetentionWithStrategyBlockSchema() resourceSchema.ListNestedBloc
191178
Build(),
192179
"unit": util.ResourceString().
193180
Optional().Computed().
194-
Validators(stringvalidator.OneOf(core.RetentionUnitDays, core.RetentionUnitItems)).
181+
Validators(stringvalidator.OneOfCaseInsensitive(core.RetentionUnitDays, core.RetentionUnitItems)).
195182
Description("The unit of quantity to keep. Valid units are Days or Items. The default value is Days.").
196183
Build(),
197184
},
@@ -263,7 +250,7 @@ func (v retentionWithStrategyValidator) ValidateObject(ctx context.Context, req
263250

264251
func GetResourceRetentionBlockSchema() resourceSchema.ListNestedBlock {
265252
return resourceSchema.ListNestedBlock{
266-
DeprecationMessage: "This block will depreciate when octopus 2025.3 is no longer supported. Please use the `release_retention_with_strategy` and `tentacle_retention_with_strategy` blocks instead.",
253+
DeprecationMessage: "This block will deprecate when octopus 2025.3 is no longer supported. Please use the `release_retention_with_strategy` and `tentacle_retention_with_strategy` blocks instead.",
267254
Description: "Defines the retention policy for releases or tentacles.",
268255
NestedObject: resourceSchema.NestedBlockObject{
269256
Attributes: map[string]resourceSchema.Attribute{
@@ -345,6 +332,7 @@ func (v retentionValidator) ValidateObject(ctx context.Context, req validator.Ob
345332
}
346333
}
347334
}
335+
348336
func GetRetentionAttribute() datasourceSchema.ListNestedAttribute {
349337
return datasourceSchema.ListNestedAttribute{
350338
Computed: true,
@@ -357,3 +345,15 @@ func GetRetentionAttribute() datasourceSchema.ListNestedAttribute {
357345
},
358346
}
359347
}
348+
func getRetentionWithStrategyAttribute() datasourceSchema.ListNestedAttribute {
349+
return datasourceSchema.ListNestedAttribute{
350+
Computed: true,
351+
NestedObject: datasourceSchema.NestedAttributeObject{
352+
Attributes: map[string]datasourceSchema.Attribute{
353+
"strategy": util.DataSourceString().Computed().Description("The retention strategy.").Build(),
354+
"quantity_to_keep": util.DataSourceInt64().Computed().Description("The quantity of releases to keep.").Build(),
355+
"unit": util.DataSourceString().Computed().Description("The unit of time for the retention policy.").Build(),
356+
},
357+
},
358+
}
359+
}

0 commit comments

Comments
 (0)