Skip to content

Commit

Permalink
Add support retrieve tenant scope in deployment freeze datasournce
Browse files Browse the repository at this point in the history
  • Loading branch information
HuyPhanNguyen committed Dec 9, 2024
1 parent 11e25de commit 5f4a2db
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 89 deletions.
11 changes: 11 additions & 0 deletions docs/data-sources/deployment_freezes.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Read-Only:
- `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`
Expand All @@ -60,3 +61,13 @@ Read-Only:
- `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


94 changes: 55 additions & 39 deletions octopusdeploy_framework/datasource_deployment_freeze.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,6 @@ import (

const deploymentFreezeDatasourceName = "deployment_freezes"

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"`
Expand Down Expand Up @@ -114,27 +92,55 @@ 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
}

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,
"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, diags := types.ListValueFrom(ctx, types.StringType, freeze.RecurringSchedule.DaysOfWeek)
if diags.HasError() {
daysOfWeek, daysDiags := types.ListValueFrom(ctx, types.StringType, freeze.RecurringSchedule.DaysOfWeek)
if daysDiags.HasError() {
diags.Append(daysDiags...)
return nil, diags
}

Expand Down Expand Up @@ -181,8 +187,9 @@ func mapFreezeToAttribute(ctx context.Context, freeze deploymentfreezes.Deployme
"day_of_week": dayOfWeek,
}

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

Expand All @@ -209,13 +216,22 @@ func freezeRecurringScheduleObjectType() map[string]attr.Type {
}
}

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}},
"recurring_schedule": types.ObjectType{AttrTypes: freezeRecurringScheduleObjectType()},
"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()},
}
}
160 changes: 115 additions & 45 deletions octopusdeploy_framework/datasource_deployment_freeze_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import (
func TestAccDataSourceDeploymentFreezes(t *testing.T) {
spaceName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
freezeName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
resourceName := "data.octopusdeploy_deployment_freezes.test_freeze"
tenantName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
projectName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
environmentName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
dataSourceName := "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)

Expand All @@ -22,69 +24,137 @@ func TestAccDataSourceDeploymentFreezes(t *testing.T) {
ProtoV6ProviderFactories: ProtoV6ProviderFactories(),
Steps: []resource.TestStep{
{
Config: testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName, startTime, endTime),
Config: testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName, startTime, endTime, false, projectName, environmentName, tenantName),
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"),
resource.TestCheckResourceAttrSet(dataSourceName, "id"),
resource.TestCheckResourceAttr(dataSourceName, "partial_name", freezeName),
resource.TestCheckResourceAttr(dataSourceName, "deployment_freezes.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "deployment_freezes.0.name", freezeName),
resource.TestCheckResourceAttr(dataSourceName, "deployment_freezes.0.tenant_project_environment_scope.#", "0"),
),
},
{
Config: testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName, startTime, endTime, true, projectName, environmentName, tenantName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(dataSourceName, "id"),
resource.TestCheckResourceAttr(dataSourceName, "deployment_freezes.0.tenant_project_environment_scope.#", "1"),
resource.TestCheckResourceAttrSet(dataSourceName, "deployment_freezes.0.tenant_project_environment_scope.0.tenant_id"),
resource.TestCheckResourceAttrSet(dataSourceName, "deployment_freezes.0.tenant_project_environment_scope.0.project_id"),
resource.TestCheckResourceAttrSet(dataSourceName, "deployment_freezes.0.tenant_project_environment_scope.0.environment_id"),
),
},
},
})
}

func testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName string, startTime, endTime time.Time) string {
return fmt.Sprintf(`
func testAccDataSourceDeploymentFreezesConfig(spaceName, freezeName string, startTime, endTime time.Time, includeTenant bool, projectName, environmentName, tenantName string) string {
baseConfig := 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"]
name = "%s"
is_default = false
is_task_queue_stopped = false
description = "Test space for deployment freeze datasource"
space_managers_teams = ["teams-administrators"]
}
resource "octopusdeploy_environment" "test_environment" {
name = "%s"
description = "Test environment"
space_id = octopusdeploy_space.test_space.id
}
resource "octopusdeploy_project_group" "test_project_group" {
name = "Test Project Group"
description = "Test project group for deployment freeze datasource"
space_id = octopusdeploy_space.test_space.id
}
resource "octopusdeploy_lifecycle" "test_lifecycle" {
name = "Test Lifecycle"
space_id = octopusdeploy_space.test_space.id
}
resource "octopusdeploy_project" "test_project" {
name = "%s"
lifecycle_id = octopusdeploy_lifecycle.test_lifecycle.id
project_group_id = octopusdeploy_project_group.test_project_group.id
space_id = octopusdeploy_space.test_space.id
}
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"]
}
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]
}
`, spaceName, environmentName, projectName, freezeName, startTime.Format(time.RFC3339), endTime.Format(time.RFC3339))

if includeTenant {
tenantConfig := fmt.Sprintf(`
resource "octopusdeploy_tenant" "test_tenant" {
name = "%s"
space_id = octopusdeploy_space.test_space.id
}
resource "octopusdeploy_tenant_project" "test_tenant_project" {
tenant_id = octopusdeploy_tenant.test_tenant.id
project_id = octopusdeploy_project.test_project.id
environment_ids = [octopusdeploy_environment.test_environment.id]
space_id = octopusdeploy_space.test_space.id
}
resource "octopusdeploy_deployment_freeze_tenant" "test_tenant_scope" {
deploymentfreeze_id = octopusdeploy_deployment_freeze.test_freeze.id
tenant_id = octopusdeploy_tenant.test_tenant.id
project_id = octopusdeploy_project.test_project.id
environment_id = octopusdeploy_environment.test_environment.id
depends_on = [octopusdeploy_space.test_space]
depends_on = [
octopusdeploy_tenant_project.test_tenant_project
]
}
`, tenantName)
baseConfig = baseConfig + tenantConfig
}

datasourceConfig := `
data "octopusdeploy_deployment_freezes" "test_freeze" {
ids = null
partial_name = "%s"
skip = 0
take = 1
depends_on = [octopusdeploy_deployment_freeze.test_freeze]
ids = null
partial_name = "%s"
skip = 0
take = 1
depends_on = [`

if includeTenant {
datasourceConfig += `
octopusdeploy_deployment_freeze.test_freeze,
octopusdeploy_deployment_freeze_tenant.test_tenant_scope
`
} else {
datasourceConfig += `
octopusdeploy_deployment_freeze.test_freeze
`
}

datasourceConfig += `]
}
output "octopus_space_id" {
value = octopusdeploy_space.test_space.id
value = octopusdeploy_space.test_space.id
}
output "octopus_freeze_id" {
value = data.octopusdeploy_deployment_freezes.test_freeze.deployment_freezes[0].id
value = data.octopusdeploy_deployment_freezes.test_freeze.deployment_freezes[0].id
}
`, spaceName, freezeName, startTime.Format(time.RFC3339), endTime.Format(time.RFC3339), freezeName)
`
return baseConfig + fmt.Sprintf(datasourceConfig, freezeName)
}
Loading

0 comments on commit 5f4a2db

Please sign in to comment.