Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support deployment freeze recurring schedule #840

Merged
merged 15 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions docs/data-sources/deployment_freezes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,29 @@ description: |-

Provides information about deployment freezes

## Example Usage

```terraform
data "octopusdeploy_deployment_freezes" "test_freeze" {
ids = null
partial_name = "Freeze Name"
skip = 5
take = 100
}


data "octopusdeploy_deployment_freezes" "project_freezes" {
project_ids = ["projects-1"]
skip = 0
take = 5
}

data "octopusdeploy_deployment_freezes" "tenant_freezes" {
tenant_ids = ["tenants-1"]
skip = 0
take = 10
}
```

<!-- schema generated by tfplugindocs -->
## Schema
Expand All @@ -25,6 +47,7 @@ Provides information about deployment freezes
- `skip` (Number) A filter to specify the number of items to skip in the response.
- `status` (String) Filter by the status of the deployment freeze, value values are Expired, Active, Scheduled (case-insensitive)
- `take` (Number) A filter to specify the number of items to take (or return) in the response.
- `tenant_ids` (List of String) A filter to search by a list of tenant IDs

### Read-Only

Expand All @@ -40,6 +63,34 @@ Read-Only:
- `id` (String) The unique ID for this resource.
- `name` (String) The name of this resource.
- `project_environment_scope` (Map of List of String) The project environment scope of the deployment freeze
- `recurring_schedule` (Attributes) (see [below for nested schema](#nestedatt--deployment_freezes--recurring_schedule))
- `start` (String) The start time of the freeze
- `tenant_project_environment_scope` (Attributes List) The tenant project environment scope of the deployment freeze (see [below for nested schema](#nestedatt--deployment_freezes--tenant_project_environment_scope))

<a id="nestedatt--deployment_freezes--recurring_schedule"></a>
### Nested Schema for `deployment_freezes.recurring_schedule`

Read-Only:

- `date_of_month` (String) The date of the month for monthly schedules
- `day_number_of_month` (String) The day number of the month for monthly schedules
- `day_of_week` (String) The day of the week for monthly schedules
- `days_of_week` (List of String) List of days of the week for weekly schedules
- `end_after_occurrences` (Number) Number of occurrences after which the schedule should end
- `end_on_date` (String) The date when the recurring schedule should end
- `end_type` (String) When the recurring schedule should end (Never, OnDate, AfterOccurrences)
- `monthly_schedule_type` (String) Type of monthly schedule (DayOfMonth, DateOfMonth)
- `type` (String) Type of recurring schedule (OnceDaily, DaysPerWeek, DaysPerMonth, Annually)
- `unit` (Number) The unit value for the schedule


<a id="nestedatt--deployment_freezes--tenant_project_environment_scope"></a>
### Nested Schema for `deployment_freezes.tenant_project_environment_scope`

Read-Only:

- `environment_id` (String) The environment ID
- `project_id` (String) The project ID
- `tenant_id` (String) The tenant ID


35 changes: 35 additions & 0 deletions docs/resources/deployment_freeze.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ resource "octopusdeploy_deployment_freeze" "freeze" {
end = "2024-12-27T00:00:00+08:00"
}

# Freeze recurring freeze yearly on Xmas
resource "octopusdeploy_deployment_freeze" "freeze" {
name = "Xmas"
start = "2024-12-25T00:00:00+10:00"
end = "2024-12-27T00:00:00+08:00"
recurring_schedule = {
type = "Annually"
unit = 1
end_type = "Never"
}
}

resource "octopusdeploy_deployment_freeze_project" "project_freeze" {
deploymentfreeze_id= octopusdeploy_deployment_freeze.freeze.id
project_id = "Projects-123"
Expand Down Expand Up @@ -69,8 +81,31 @@ resource "octopusdeploy_deployment_freeze_tenant" "tenant_freeze" {
- `name` (String) The name of this resource.
- `start` (String) The start time of the freeze, must be RFC3339 format

### Optional

- `recurring_schedule` (Attributes) (see [below for nested schema](#nestedatt--recurring_schedule))

### Read-Only

- `id` (String) The unique ID for this resource.

<a id="nestedatt--recurring_schedule"></a>
### Nested Schema for `recurring_schedule`

Required:

- `end_type` (String) When the recurring schedule should end (Never, OnDate, AfterOccurrences)
- `type` (String) Type of recurring schedule (Daily, Weekly, Monthly, Annually)
- `unit` (Number) The unit value for the schedule

Optional:

- `date_of_month` (String) The date of the month for monthly schedules
- `day_number_of_month` (String) Specifies which weekday position in the month. Valid values: 1 (First), 2 (Second), 3 (Third), 4 (Fourth), L (Last). Used with day_of_week
- `day_of_week` (String) The day of the week for monthly schedules when using DayOfMonth type
- `days_of_week` (List of String) List of days of the week for weekly schedules. Must follow order: Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
- `end_after_occurrences` (Number) Number of occurrences after which the schedule should end
- `end_on_date` (String) The date when the recurring schedule should end
- `monthly_schedule_type` (String) Type of monthly schedule (DayOfMonth, DateOfMonth)


Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
data "octopusdeploy_deployment_freezes" "test_freeze" {
ids = null
partial_name = "Freeze Name"
skip = 5
take = 100
}


data "octopusdeploy_deployment_freezes" "project_freezes" {
project_ids = ["projects-1"]
skip = 0
take = 5
}

data "octopusdeploy_deployment_freezes" "tenant_freezes" {
tenant_ids = ["tenants-1"]
skip = 0
take = 10
}
12 changes: 12 additions & 0 deletions examples/resources/octopusdeploy_deployment_freeze/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ resource "octopusdeploy_deployment_freeze" "freeze" {
end = "2024-12-27T00:00:00+08:00"
}

# Freeze recurring freeze yearly on Xmas
resource "octopusdeploy_deployment_freeze" "freeze" {
name = "Xmas"
start = "2024-12-25T00:00:00+10:00"
end = "2024-12-27T00:00:00+08:00"
recurring_schedule = {
type = "Annually"
unit = 1
end_type = "Never"
}
}

resource "octopusdeploy_deployment_freeze_project" "project_freeze" {
deploymentfreeze_id= octopusdeploy_deployment_freeze.freeze.id
project_id = "Projects-123"
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22
toolchain go1.22.3

require (
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.63.1
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.64.0
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20241206032352-dbc62b2d16cf
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4VDhOQ0Ven0=
github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.63.1 h1:ShfJ3VZqNblU5V0UnA/UNjQuUrgkljZdDp9Vxh8rhcI=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.63.1/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.64.0 h1:NWqQ/7JLUfEJQ8QHrkek7AfePuN121+f6+tUi3xP6vE=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.64.0/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20241206032352-dbc62b2d16cf h1:wuUJ6DbSZEHE4a3SfSJIcoeTQCSI6lbQ+i46ibY14+Q=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20241206032352-dbc62b2d16cf/go.mod h1:xVv8DvYhhwxtQUQQDfOYA6CY8KTkHXccxQ2RfRj6IJ0=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
Expand Down
137 changes: 121 additions & 16 deletions octopusdeploy_framework/datasource_deployment_freeze.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import (

const deploymentFreezeDatasourceName = "deployment_freezes"

type deploymentFreezesModel struct {
type deploymentFreezesDatasourceModel struct {
ID types.String `tfsdk:"id"`
IDs types.List `tfsdk:"ids"`
PartialName types.String `tfsdk:"partial_name"`
ProjectIDs types.List `tfsdk:"project_ids"`
EnvironmentIDs types.List `tfsdk:"environment_ids"`
TenantIDs types.List `tfsdk:"tenant_ids"`
IncludeComplete types.Bool `tfsdk:"include_complete"`
Status types.String `tfsdk:"status"`
Skip types.Int64 `tfsdk:"skip"`
Expand Down Expand Up @@ -48,7 +49,7 @@ func (d *deploymentFreezeDataSource) Schema(ctx context.Context, req datasource.
}

func (d *deploymentFreezeDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data deploymentFreezesModel
var data deploymentFreezesDatasourceModel
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
Expand All @@ -59,6 +60,7 @@ func (d *deploymentFreezeDataSource) Read(ctx context.Context, req datasource.Re
PartialName: data.PartialName.ValueString(),
ProjectIds: util.Ternary(data.ProjectIDs.IsNull(), []string{}, util.ExpandStringList(data.ProjectIDs)),
EnvironmentIds: util.Ternary(data.EnvironmentIDs.IsNull(), []string{}, util.ExpandStringList(data.EnvironmentIDs)),
TenantIds: util.Ternary(data.TenantIDs.IsNull(), []string{}, util.ExpandStringList(data.TenantIDs)),
IncludeComplete: data.IncludeComplete.ValueBool(),
Status: data.Status.ValueString(),
Skip: int(data.Skip.ValueInt64()),
Expand Down Expand Up @@ -92,31 +94,134 @@ var _ datasource.DataSource = &deploymentFreezeDataSource{}
var _ datasource.DataSourceWithConfigure = &deploymentFreezeDataSource{}

func mapFreezeToAttribute(ctx context.Context, freeze deploymentfreezes.DeploymentFreeze) (attr.Value, diag.Diagnostics) {
var diags diag.Diagnostics

projectScopes := make(map[string]attr.Value)
for projectId, environmentScopes := range freeze.ProjectEnvironmentScope {
projectScopes[projectId] = util.FlattenStringList(environmentScopes)
}

scopeType, diags := types.MapValueFrom(ctx, types.ListType{ElemType: types.StringType}, projectScopes)
if diags.HasError() {
scopeType, scopeDiags := types.MapValueFrom(ctx, types.ListType{ElemType: types.StringType}, projectScopes)
if scopeDiags.HasError() {
diags.Append(scopeDiags...)
return nil, diags
}

return types.ObjectValueMust(freezeObjectType(), map[string]attr.Value{
"id": types.StringValue(freeze.ID),
"name": types.StringValue(freeze.Name),
"start": types.StringValue(freeze.Start.Format(time.RFC3339)),
"end": types.StringValue(freeze.End.Format(time.RFC3339)),
"project_environment_scope": scopeType,
}), diags
tenantScopes := make([]attr.Value, 0)
for _, scope := range freeze.TenantProjectEnvironmentScope {
tenantScope, tDiags := types.ObjectValue(tenantScopeObjectType(), map[string]attr.Value{
"tenant_id": types.StringValue(scope.TenantId),
"project_id": types.StringValue(scope.ProjectId),
"environment_id": types.StringValue(scope.EnvironmentId),
})
if tDiags.HasError() {
diags.Append(tDiags...)
return nil, diags
}
tenantScopes = append(tenantScopes, tenantScope)
}

tenantScopesList, tsDiags := types.ListValue(
types.ObjectType{AttrTypes: tenantScopeObjectType()},
tenantScopes,
)
if tsDiags.HasError() {
diags.Append(tsDiags...)
return nil, diags
}

attrs := map[string]attr.Value{
"id": types.StringValue(freeze.ID),
"name": types.StringValue(freeze.Name),
"start": types.StringValue(freeze.Start.Format(time.RFC3339)),
"end": types.StringValue(freeze.End.Format(time.RFC3339)),
"project_environment_scope": scopeType,
"tenant_project_environment_scope": tenantScopesList,
}

if freeze.RecurringSchedule != nil {
daysOfWeek, daysDiags := types.ListValueFrom(ctx, types.StringType, freeze.RecurringSchedule.DaysOfWeek)
if daysDiags.HasError() {
diags.Append(daysDiags...)
return nil, diags
}

endOnDate := types.StringNull()
if freeze.RecurringSchedule.EndOnDate != nil {
endOnDate = types.StringValue(freeze.RecurringSchedule.EndOnDate.Format(time.RFC3339))
}

endAfterOccurrences := types.Int64Value(int64(freeze.RecurringSchedule.EndAfterOccurrences))

monthlyScheduleType := types.StringNull()
if freeze.RecurringSchedule.MonthlyScheduleType != "" {
monthlyScheduleType = types.StringValue(freeze.RecurringSchedule.MonthlyScheduleType)
}

dateOfMonth := types.StringValue(freeze.RecurringSchedule.DateOfMonth)

dayNumberOfMonth := types.StringValue(freeze.RecurringSchedule.DayNumberOfMonth)

dayOfWeek := types.StringValue(freeze.RecurringSchedule.DayOfWeek)

scheduleAttrs := map[string]attr.Value{
"type": types.StringValue(string(freeze.RecurringSchedule.Type)),
"unit": types.Int64Value(int64(freeze.RecurringSchedule.Unit)),
"end_type": types.StringValue(string(freeze.RecurringSchedule.EndType)),
"end_on_date": endOnDate,
"end_after_occurrences": endAfterOccurrences,
"monthly_schedule_type": monthlyScheduleType,
"date_of_month": dateOfMonth,
"day_number_of_month": dayNumberOfMonth,
"days_of_week": daysOfWeek,
"day_of_week": dayOfWeek,
}

recurringSchedule, rsDiags := types.ObjectValue(freezeRecurringScheduleObjectType(), scheduleAttrs)
if rsDiags.HasError() {
diags.Append(rsDiags...)
return nil, diags
}

attrs["recurring_schedule"] = recurringSchedule
} else {
attrs["recurring_schedule"] = types.ObjectNull(freezeRecurringScheduleObjectType())
}

return types.ObjectValueMust(freezeObjectType(), attrs), diags
}

func freezeRecurringScheduleObjectType() map[string]attr.Type {
return map[string]attr.Type{
"type": types.StringType,
"unit": types.Int64Type,
"end_type": types.StringType,
"end_on_date": types.StringType,
"end_after_occurrences": types.Int64Type,
"monthly_schedule_type": types.StringType,
"date_of_month": types.StringType,
"day_number_of_month": types.StringType,
"days_of_week": types.ListType{ElemType: types.StringType},
"day_of_week": types.StringType,
}
}

func tenantScopeObjectType() map[string]attr.Type {
return map[string]attr.Type{
"tenant_id": types.StringType,
"project_id": types.StringType,
"environment_id": types.StringType,
}
}

func freezeObjectType() map[string]attr.Type {
return map[string]attr.Type{
"id": types.StringType,
"name": types.StringType,
"start": types.StringType,
"end": types.StringType,
"project_environment_scope": types.MapType{ElemType: types.ListType{ElemType: types.StringType}},
"id": types.StringType,
"name": types.StringType,
"start": types.StringType,
"end": types.StringType,
"project_environment_scope": types.MapType{ElemType: types.ListType{ElemType: types.StringType}},
"tenant_project_environment_scope": types.ListType{ElemType: types.ObjectType{AttrTypes: tenantScopeObjectType()}},
"recurring_schedule": types.ObjectType{AttrTypes: freezeRecurringScheduleObjectType()},
}
}
Loading
Loading