From 11e25deecb5ff762af9d572565cfbd0acd192aaf Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 9 Dec 2024 11:17:32 +1000 Subject: [PATCH] Add recurring schedule to deployment freeze data source and add test --- docs/data-sources/deployment_freezes.md | 17 +++ .../datasource_deployment_freeze.go | 107 +++++++++++++++++- .../datasource_deployment_freeze_test.go | 90 +++++++++++++++ .../schemas/deployment_freeze.go | 49 +++++++- 4 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 octopusdeploy_framework/datasource_deployment_freeze_test.go diff --git a/docs/data-sources/deployment_freezes.md b/docs/data-sources/deployment_freezes.md index a4a365ba..b23bd14f 100644 --- a/docs/data-sources/deployment_freezes.md +++ b/docs/data-sources/deployment_freezes.md @@ -40,6 +40,23 @@ 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 + +### 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 + diff --git a/octopusdeploy_framework/datasource_deployment_freeze.go b/octopusdeploy_framework/datasource_deployment_freeze.go index e02e71e2..b3fb8d3d 100644 --- a/octopusdeploy_framework/datasource_deployment_freeze.go +++ b/octopusdeploy_framework/datasource_deployment_freeze.go @@ -14,7 +14,29 @@ import ( const deploymentFreezeDatasourceName = "deployment_freezes" -type deploymentFreezesModel struct { +type recurringScheduleDatasourceModel struct { + Type types.String `tfsdk:"type"` + Unit types.Int64 `tfsdk:"unit"` + EndType types.String `tfsdk:"end_type"` + EndOnDate types.String `tfsdk:"end_on_date"` + EndAfterOccurrences types.Int64 `tfsdk:"end_after_occurrences"` + MonthlyScheduleType types.String `tfsdk:"monthly_schedule_type"` + DateOfMonth types.String `tfsdk:"date_of_month"` + DayNumberOfMonth types.String `tfsdk:"day_number_of_month"` + DaysOfWeek types.List `tfsdk:"days_of_week"` + DayOfWeek types.String `tfsdk:"day_of_week"` +} + +type deploymentFreezeDatasourceModel struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Start types.String `tfsdk:"start"` + End types.String `tfsdk:"end"` + ProjectEnvironmentScope types.Map `tfsdk:"project_environment_scope"` + RecurringSchedule *recurringScheduleDatasourceModel `tfsdk:"recurring_schedule"` +} + +type deploymentFreezesDatasourceModel struct { ID types.String `tfsdk:"id"` IDs types.List `tfsdk:"ids"` PartialName types.String `tfsdk:"partial_name"` @@ -48,7 +70,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 @@ -102,13 +124,89 @@ func mapFreezeToAttribute(ctx context.Context, freeze deploymentfreezes.Deployme return nil, diags } - return types.ObjectValueMust(freezeObjectType(), map[string]attr.Value{ + 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, - }), diags + } + + if freeze.RecurringSchedule != nil { + daysOfWeek, diags := types.ListValueFrom(ctx, types.StringType, freeze.RecurringSchedule.DaysOfWeek) + if diags.HasError() { + return nil, diags + } + + endOnDate := types.StringNull() + if freeze.RecurringSchedule.EndOnDate != nil { + endOnDate = types.StringValue(freeze.RecurringSchedule.EndOnDate.Format(time.RFC3339)) + } + + endAfterOccurrences := types.Int64Null() + if freeze.RecurringSchedule.EndAfterOccurrences != nil { + endAfterOccurrences = types.Int64Value(int64(*freeze.RecurringSchedule.EndAfterOccurrences)) + } + + monthlyScheduleType := types.StringNull() + if freeze.RecurringSchedule.MonthlyScheduleType != "" { + monthlyScheduleType = types.StringValue(freeze.RecurringSchedule.MonthlyScheduleType) + } + + dateOfMonth := types.StringNull() + if freeze.RecurringSchedule.DateOfMonth != nil { + dateOfMonth = types.StringValue(*freeze.RecurringSchedule.DateOfMonth) + } + + dayNumberOfMonth := types.StringNull() + if freeze.RecurringSchedule.DayNumberOfMonth != nil { + dayNumberOfMonth = types.StringValue(*freeze.RecurringSchedule.DayNumberOfMonth) + } + + dayOfWeek := types.StringNull() + if freeze.RecurringSchedule.DayOfWeek != nil { + 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, diags := types.ObjectValue(freezeRecurringScheduleObjectType(), scheduleAttrs) + if diags.HasError() { + 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 freezeObjectType() map[string]attr.Type { @@ -118,5 +216,6 @@ func freezeObjectType() map[string]attr.Type { "start": types.StringType, "end": types.StringType, "project_environment_scope": types.MapType{ElemType: types.ListType{ElemType: types.StringType}}, + "recurring_schedule": types.ObjectType{AttrTypes: freezeRecurringScheduleObjectType()}, } } diff --git a/octopusdeploy_framework/datasource_deployment_freeze_test.go b/octopusdeploy_framework/datasource_deployment_freeze_test.go new file mode 100644 index 00000000..b5ad302b --- /dev/null +++ b/octopusdeploy_framework/datasource_deployment_freeze_test.go @@ -0,0 +1,90 @@ +package octopusdeploy_framework + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "testing" + "time" +) + +func TestAccDataSourceDeploymentFreezes(t *testing.T) { + spaceName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + freezeName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + resourceName := "data.octopusdeploy_deployment_freezes.test_freeze" + + // Use future dates for the freeze period, ensuring UTC timezone + startTime := time.Now().AddDate(1, 0, 0).UTC() + endTime := startTime.Add(24 * time.Hour) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { TestAccPreCheck(t) }, + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName, startTime, endTime), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "partial_name", freezeName), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "deployment_freezes.0.id"), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.name", freezeName), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.start", startTime.Format(time.RFC3339)), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.end", endTime.Format(time.RFC3339)), + resource.TestCheckResourceAttrSet(resourceName, "deployment_freezes.0.project_environment_scope.%"), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.recurring_schedule.type", "DaysPerWeek"), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.recurring_schedule.unit", "24"), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.recurring_schedule.end_type", "AfterOccurrences"), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.recurring_schedule.end_after_occurrences", "5"), + resource.TestCheckResourceAttr(resourceName, "deployment_freezes.0.recurring_schedule.days_of_week.#", "3"), + testAccCheckOutputExists("octopus_space_id"), + testAccCheckOutputExists("octopus_freeze_id"), + ), + }, + }, + }) +} + +func testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName string, startTime, endTime time.Time) string { + return fmt.Sprintf(` +resource "octopusdeploy_space" "test_space" { + name = "%s" + is_default = false + is_task_queue_stopped = false + description = "Test space for deployment freeze datasource" + space_managers_teams = ["teams-administrators"] +} + +resource "octopusdeploy_deployment_freeze" "test_freeze" { + name = "%s" + start = "%s" + end = "%s" + + recurring_schedule = { + type = "DaysPerWeek" + unit = 24 + end_type = "AfterOccurrences" + end_after_occurrences = 5 + days_of_week = ["Monday", "Wednesday", "Friday"] + } + + depends_on = [octopusdeploy_space.test_space] +} + +data "octopusdeploy_deployment_freezes" "test_freeze" { + ids = null + partial_name = "%s" + skip = 0 + take = 1 + depends_on = [octopusdeploy_deployment_freeze.test_freeze] +} + +output "octopus_space_id" { + value = octopusdeploy_space.test_space.id +} + +output "octopus_freeze_id" { + value = data.octopusdeploy_deployment_freezes.test_freeze.deployment_freezes[0].id +} +`, spaceName, freezeName, startTime.Format(time.RFC3339), endTime.Format(time.RFC3339), freezeName) +} diff --git a/octopusdeploy_framework/schemas/deployment_freeze.go b/octopusdeploy_framework/schemas/deployment_freeze.go index f03c515d..3d986d7a 100644 --- a/octopusdeploy_framework/schemas/deployment_freeze.go +++ b/octopusdeploy_framework/schemas/deployment_freeze.go @@ -61,7 +61,6 @@ func (d DeploymentFreezeSchema) GetResourceSchema() resourceSchema.Schema { }, } } - func (d DeploymentFreezeSchema) GetDatasourceSchema() datasourceSchema.Schema { return datasourceSchema.Schema{ Description: "Provides information about deployment freezes", @@ -107,6 +106,53 @@ func (d DeploymentFreezeSchema) GetDatasourceSchema() datasourceSchema.Schema { Optional: false, Computed: true, }, + "recurring_schedule": datasourceSchema.SingleNestedAttribute{ + Computed: true, + Optional: false, + Attributes: map[string]datasourceSchema.Attribute{ + "type": datasourceSchema.StringAttribute{ + Description: "Type of recurring schedule (OnceDaily, DaysPerWeek, DaysPerMonth, Annually)", + Computed: true, + }, + "unit": datasourceSchema.Int64Attribute{ + Description: "The unit value for the schedule", + Computed: true, + }, + "end_type": datasourceSchema.StringAttribute{ + Description: "When the recurring schedule should end (Never, OnDate, AfterOccurrences)", + Computed: true, + }, + "end_on_date": datasourceSchema.StringAttribute{ + Description: "The date when the recurring schedule should end", + Computed: true, + }, + "end_after_occurrences": datasourceSchema.Int64Attribute{ + Description: "Number of occurrences after which the schedule should end", + Computed: true, + }, + "monthly_schedule_type": datasourceSchema.StringAttribute{ + Description: "Type of monthly schedule (DayOfMonth, DateOfMonth)", + Computed: true, + }, + "date_of_month": datasourceSchema.StringAttribute{ + Description: "The date of the month for monthly schedules", + Computed: true, + }, + "day_number_of_month": datasourceSchema.StringAttribute{ + Description: "The day number of the month for monthly schedules", + Computed: true, + }, + "days_of_week": datasourceSchema.ListAttribute{ + Description: "List of days of the week for weekly schedules", + Computed: true, + ElementType: types.StringType, + }, + "day_of_week": datasourceSchema.StringAttribute{ + Description: "The day of the week for monthly schedules", + Computed: true, + }, + }, + }, }, }, Optional: false, @@ -114,7 +160,6 @@ func (d DeploymentFreezeSchema) GetDatasourceSchema() datasourceSchema.Schema { }, }, } - } var _ EntitySchema = &DeploymentFreezeSchema{}