From 7fade0508fa4f3279c9ef91caa3e97bb6c781464 Mon Sep 17 00:00:00 2001 From: Huy Nguyen <162080607+HuyPhanNguyen@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:25:25 +1000 Subject: [PATCH] Update schema and doc GitCredential, Lifecycles and TenantProjectVariable (#717) * Update lifecycles schema and doc * Fix lifecyle schema * Update git credential resource * Refactor tenant project variable * Make it consistent * Update datasource GitCredentialModel * fix test fail * Add username_password_account resource * Revert "Add username_password_account resource" This reverts commit 7a20ecb5c8698d7f341c2437d5f841aa2cf179ae. * Update doc example * small change on lifecycle example --- docs/data-sources/lifecycles.md | 70 ++++---- docs/resources/git_credential.md | 10 +- docs/resources/lifecycle.md | 38 ++--- docs/resources/tenant_project_variable.md | 18 +- .../datasource_git_credentials.go | 8 +- .../schemas/gitCredential.go | 95 +++++------ octopusdeploy_framework/schemas/lifecycle.go | 155 +++++++++--------- .../schemas/tenant_project_variable.go | 44 +++-- .../util/resource_attribute_builder.go | 80 +++++++++ 9 files changed, 307 insertions(+), 211 deletions(-) diff --git a/docs/data-sources/lifecycles.md b/docs/data-sources/lifecycles.md index 2d6ce478e..d7fb232f4 100644 --- a/docs/data-sources/lifecycles.md +++ b/docs/data-sources/lifecycles.md @@ -26,62 +26,62 @@ data "octopusdeploy_lifecycles" "example" { ### Optional -- `ids` (List of String) A filter to search by a list of IDs. -- `partial_name` (String) A filter to search by the partial match of a name. +- `ids` (List of String) A list of lifecycle IDs to filter by. +- `partial_name` (String) A partial name to filter lifecycles by. - `skip` (Number) A filter to specify the number of items to skip in the response. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this lifecycle. - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `id` (String) The ID of this resource. -- `lifecycles` (Block List) A list of lifecycles that match the filter(s). (see [below for nested schema](#nestedblock--lifecycles)) +- `id` (String) The ID of the lifecycle. +- `lifecycles` (Attributes List) (see [below for nested schema](#nestedatt--lifecycles)) - + ### Nested Schema for `lifecycles` Read-Only: -- `description` (String) The description of this lifecycle. -- `id` (String) The unique ID for this resource. -- `name` (String) The name of this resource. -- `phase` (List of Object) (see [below for nested schema](#nestedatt--lifecycles--phase)) -- `release_retention_policy` (List of Object) (see [below for nested schema](#nestedatt--lifecycles--release_retention_policy)) -- `space_id` (String) The space ID associated with this resource. -- `tentacle_retention_policy` (List of Object) (see [below for nested schema](#nestedatt--lifecycles--tentacle_retention_policy)) +- `description` (String) The description of the lifecycle. +- `id` (String) The ID of the lifecycle. +- `name` (String) The name of the lifecycle. +- `phase` (Attributes List) (see [below for nested schema](#nestedatt--lifecycles--phase)) +- `release_retention_policy` (Attributes List) (see [below for nested schema](#nestedatt--lifecycles--release_retention_policy)) +- `space_id` (String) The space ID associated with this lifecycle. +- `tentacle_retention_policy` (Attributes List) (see [below for nested schema](#nestedatt--lifecycles--tentacle_retention_policy)) ### Nested Schema for `lifecycles.phase` Read-Only: -- `automatic_deployment_targets` (List of String) -- `id` (String) -- `is_optional_phase` (Boolean) -- `minimum_environments_before_promotion` (Number) -- `name` (String) -- `optional_deployment_targets` (List of String) -- `release_retention_policy` (List of Object) (see [below for nested schema](#nestedobjatt--lifecycles--phase--release_retention_policy)) -- `tentacle_retention_policy` (List of Object) (see [below for nested schema](#nestedobjatt--lifecycles--phase--tentacle_retention_policy)) +- `automatic_deployment_targets` (List of String) The automatic deployment targets for this phase. +- `id` (String) The ID of the phase. +- `is_optional_phase` (Boolean) Whether this phase is optional. +- `minimum_environments_before_promotion` (Number) The minimum number of environments before promotion. +- `name` (String) The name of the phase. +- `optional_deployment_targets` (List of String) The optional deployment targets for this phase. +- `release_retention_policy` (Attributes List) (see [below for nested schema](#nestedatt--lifecycles--phase--release_retention_policy)) +- `tentacle_retention_policy` (Attributes List) (see [below for nested schema](#nestedatt--lifecycles--phase--tentacle_retention_policy)) - + ### Nested Schema for `lifecycles.phase.release_retention_policy` Read-Only: -- `quantity_to_keep` (Number) -- `should_keep_forever` (Boolean) -- `unit` (String) +- `quantity_to_keep` (Number) The quantity of releases to keep. +- `should_keep_forever` (Boolean) Whether releases should be kept forever. +- `unit` (String) The unit of time for the retention policy. - + ### Nested Schema for `lifecycles.phase.tentacle_retention_policy` Read-Only: -- `quantity_to_keep` (Number) -- `should_keep_forever` (Boolean) -- `unit` (String) +- `quantity_to_keep` (Number) The quantity of releases to keep. +- `should_keep_forever` (Boolean) Whether releases should be kept forever. +- `unit` (String) The unit of time for the retention policy. @@ -90,9 +90,9 @@ Read-Only: Read-Only: -- `quantity_to_keep` (Number) -- `should_keep_forever` (Boolean) -- `unit` (String) +- `quantity_to_keep` (Number) The quantity of releases to keep. +- `should_keep_forever` (Boolean) Whether releases should be kept forever. +- `unit` (String) The unit of time for the retention policy. @@ -100,8 +100,8 @@ Read-Only: Read-Only: -- `quantity_to_keep` (Number) -- `should_keep_forever` (Boolean) -- `unit` (String) +- `quantity_to_keep` (Number) The quantity of releases to keep. +- `should_keep_forever` (Boolean) Whether releases should be kept forever. +- `unit` (String) The unit of time for the retention policy. diff --git a/docs/resources/git_credential.md b/docs/resources/git_credential.md index e47402fa1..ad9aa4392 100644 --- a/docs/resources/git_credential.md +++ b/docs/resources/git_credential.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_git_credential Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource manages Git credentials in Octopus Deploy. + Manages a Git credential in Octopus Deploy. --- # octopusdeploy_git_credential (Resource) -This resource manages Git credentials in Octopus Deploy. +Manages a Git credential in Octopus Deploy. @@ -17,15 +17,15 @@ This resource manages Git credentials in Octopus Deploy. ### Required -- `name` (String) The name of the Git credential. This name must be unique. +- `name` (String) The name of this Git Credential. - `password` (String, Sensitive) The password for the Git credential. - `username` (String) The username for the Git credential. ### Optional -- `description` (String) The description of this Git credential. +- `description` (String) The description of this Git Credential. - `id` (String) The unique ID for this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this Git Credential. - `type` (String) The Git credential authentication type. diff --git a/docs/resources/lifecycle.md b/docs/resources/lifecycle.md index a367cce62..8714ab06e 100644 --- a/docs/resources/lifecycle.md +++ b/docs/resources/lifecycle.md @@ -33,8 +33,8 @@ resource "octopusdeploy_lifecycle" "example" { name = "foo" release_retention_policy { - quantity_to_keep = 0 - should_keep_forever = true + quantity_to_keep = 0 + should_keep_forever = true // true only if quantity_to_keep = 0 unit = "Days" } @@ -64,10 +64,10 @@ resource "octopusdeploy_lifecycle" "example" { - `description` (String) The description of this lifecycle. - `id` (String) The unique ID for this resource. -- `phase` (Block List) (see [below for nested schema](#nestedblock--phase)) -- `release_retention_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--release_retention_policy)) +- `phase` (Block List) Defines a phase in the lifecycle. (see [below for nested schema](#nestedblock--phase)) +- `release_retention_policy` (Block List) Defines the retention policy for releases or tentacles. (see [below for nested schema](#nestedblock--release_retention_policy)) - `space_id` (String) The space ID associated with this resource. -- `tentacle_retention_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--tentacle_retention_policy)) +- `tentacle_retention_policy` (Block List) Defines the retention policy for releases or tentacles. (see [below for nested schema](#nestedblock--tentacle_retention_policy)) ### Nested Schema for `phase` @@ -83,17 +83,17 @@ Optional: - `is_optional_phase` (Boolean) If false a release must be deployed to this phase before it can be deployed to the next phase. - `minimum_environments_before_promotion` (Number) The number of units required before a release can enter the next phase. If 0, all environments are required. - `optional_deployment_targets` (List of String) Environment IDs in this phase that a release can be deployed to, but is not automatically deployed to -- `release_retention_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--phase--release_retention_policy)) -- `tentacle_retention_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--phase--tentacle_retention_policy)) +- `release_retention_policy` (Block List) Defines the retention policy for releases or tentacles. (see [below for nested schema](#nestedblock--phase--release_retention_policy)) +- `tentacle_retention_policy` (Block List) Defines the retention policy for releases or tentacles. (see [below for nested schema](#nestedblock--phase--tentacle_retention_policy)) ### Nested Schema for `phase.release_retention_policy` Optional: -- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is `30`. If `0` then all are kept. -- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is `false`. -- `unit` (String) The unit of quantity to keep. Valid units are `Days` or `Items`. The default value is `Days`. +- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is 30. If 0 then all are kept. +- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is false. +- `unit` (String) The unit of quantity to keep. Valid units are Days or Items. The default value is Days. @@ -101,9 +101,9 @@ Optional: Optional: -- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is `30`. If `0` then all are kept. -- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is `false`. -- `unit` (String) The unit of quantity to keep. Valid units are `Days` or `Items`. The default value is `Days`. +- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is 30. If 0 then all are kept. +- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is false. +- `unit` (String) The unit of quantity to keep. Valid units are Days or Items. The default value is Days. @@ -112,9 +112,9 @@ Optional: Optional: -- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is `30`. If `0` then all are kept. -- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is `false`. -- `unit` (String) The unit of quantity to keep. Valid units are `Days` or `Items`. The default value is `Days`. +- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is 30. If 0 then all are kept. +- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is false. +- `unit` (String) The unit of quantity to keep. Valid units are Days or Items. The default value is Days. @@ -122,9 +122,9 @@ Optional: Optional: -- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is `30`. If `0` then all are kept. -- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is `false`. -- `unit` (String) The unit of quantity to keep. Valid units are `Days` or `Items`. The default value is `Days`. +- `quantity_to_keep` (Number) The number of days/releases to keep. The default value is 30. If 0 then all are kept. +- `should_keep_forever` (Boolean) Indicates if items should never be deleted. The default value is false. +- `unit` (String) The unit of quantity to keep. Valid units are Days or Items. The default value is Days. ## Import diff --git a/docs/resources/tenant_project_variable.md b/docs/resources/tenant_project_variable.md index c27b1afae..053b6739a 100644 --- a/docs/resources/tenant_project_variable.md +++ b/docs/resources/tenant_project_variable.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_tenant_project_variable Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource manages tenant project variables in Octopus Deploy. + Manages a tenant project variable in Octopus Deploy. --- # octopusdeploy_tenant_project_variable (Resource) -This resource manages tenant project variables in Octopus Deploy. +Manages a tenant project variable in Octopus Deploy. @@ -17,18 +17,18 @@ This resource manages tenant project variables in Octopus Deploy. ### Required -- `environment_id` (String) -- `project_id` (String) -- `template_id` (String) -- `tenant_id` (String) +- `environment_id` (String) The ID of the environment. +- `project_id` (String) The ID of the project. +- `template_id` (String) The ID of the variable template. +- `tenant_id` (String) The ID of the tenant. ### Optional -- `space_id` (String) -- `value` (String, Sensitive) +- `space_id` (String) The space ID associated with this Tenant Project Variable. +- `value` (String, Sensitive) The value of the variable. ### Read-Only -- `id` (String) The ID of this resource. +- `id` (String) The unique ID for this resource. diff --git a/octopusdeploy_framework/datasource_git_credentials.go b/octopusdeploy_framework/datasource_git_credentials.go index 210c7d83b..5dc1592c3 100644 --- a/octopusdeploy_framework/datasource_git_credentials.go +++ b/octopusdeploy_framework/datasource_git_credentials.go @@ -8,7 +8,6 @@ import ( "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" "time" ) @@ -35,6 +34,7 @@ type GitCredentialModel struct { Description types.String `tfsdk:"description"` Type types.String `tfsdk:"type"` Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` } func NewGitCredentialsDataSource() datasource.DataSource { @@ -46,10 +46,7 @@ func (g *gitCredentialsDataSource) Metadata(_ context.Context, req datasource.Me } func (g *gitCredentialsDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { - resp.Schema = schema.Schema{ - Description: "A list of Git Credentials that match the filter(s).", - Attributes: schemas.GetGitCredentialDataSourceSchema(), - } + resp.Schema = schemas.GetGitCredentialDataSourceSchema() } func (g *gitCredentialsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { @@ -102,6 +99,7 @@ func GetGitCredentialAttrTypes() map[string]attr.Type { "description": types.StringType, "type": types.StringType, "username": types.StringType, + "password": types.StringType, } } diff --git a/octopusdeploy_framework/schemas/gitCredential.go b/octopusdeploy_framework/schemas/gitCredential.go index 594f927b1..c4b0b4d6f 100644 --- a/octopusdeploy_framework/schemas/gitCredential.go +++ b/octopusdeploy_framework/schemas/gitCredential.go @@ -5,59 +5,55 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) const ( - GitCredentialResourceDescription = "Git Credential" - GitCredentialResourceName = "git_credential" - GitCredentialDatasourceName = "git_credentials" + GitCredentialResourceName = "git_credential" + GitCredentialDatasourceName = "git_credentials" ) func GetGitCredentialResourceSchema() resourceSchema.Schema { return resourceSchema.Schema{ Description: "Manages a Git credential in Octopus Deploy.", Attributes: map[string]resourceSchema.Attribute{ - "id": util.GetIdResourceSchema(), - "space_id": util.GetSpaceIdResourceSchema(GitCredentialResourceDescription), - "name": util.GetNameResourceSchema(true), - "description": util.GetDescriptionResourceSchema(GitCredentialResourceDescription), - "type": resourceSchema.StringAttribute{ - Optional: true, - Description: "The Git credential authentication type.", - }, - "username": resourceSchema.StringAttribute{ - Required: true, - Description: "The username for the Git credential.", - Validators: []validator.String{ - stringvalidator.LengthAtLeast(1), - }, - }, - "password": resourceSchema.StringAttribute{ - Required: true, - Sensitive: true, - Description: "The password for the Git credential.", - Validators: []validator.String{ - stringvalidator.LengthAtLeast(1), - }, - }, + "id": util.ResourceString().Optional().Computed().Description("The unique ID for this resource.").Build(), + "space_id": util.ResourceString().Optional().Computed().Description("The space ID associated with this Git Credential.").Build(), + "name": util.ResourceString().Required().Description("The name of this Git Credential.").Build(), + "description": util.ResourceString().Optional().Description("The description of this Git Credential.").Build(), + "type": util.ResourceString(). + Optional(). + Description("The Git credential authentication type."). + Build(), + "username": util.ResourceString(). + Required(). + Description("The username for the Git credential."). + Validators(stringvalidator.LengthAtLeast(1)). + Build(), + "password": util.ResourceString(). + Required(). + Sensitive(). + Description("The password for the Git credential."). + Validators(stringvalidator.LengthAtLeast(1)). + Build(), }, } } -func GetGitCredentialDataSourceSchema() map[string]datasourceSchema.Attribute { - return map[string]datasourceSchema.Attribute{ - "id": util.GetIdDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema(GitCredentialResourceDescription), - "name": util.GetQueryNameDatasourceSchema(), - "skip": util.GetQuerySkipDatasourceSchema(), - "take": util.GetQueryTakeDatasourceSchema(), - "git_credentials": datasourceSchema.ListNestedAttribute{ - Computed: true, - Optional: false, - Description: "A list of Git Credentials that match the filter(s).", - NestedObject: datasourceSchema.NestedAttributeObject{ - Attributes: GetGitCredentialAttributes(), +func GetGitCredentialDataSourceSchema() datasourceSchema.Schema { + return datasourceSchema.Schema{ + Description: "Use this data source to retrieve information about Git credentials in Octopus Deploy.", + Attributes: map[string]datasourceSchema.Attribute{ + "id": util.DataSourceString().Computed().Description("The unique ID for this resource.").Build(), + "space_id": util.DataSourceString().Optional().Description("The space ID associated with this Git Credential.").Build(), + "name": util.DataSourceString().Optional().Description("The name of the Git Credential to filter by.").Build(), + "skip": util.DataSourceInt64().Optional().Description("The number of records to skip.").Build(), + "take": util.DataSourceInt64().Optional().Description("The number of records to take.").Build(), + "git_credentials": datasourceSchema.ListNestedAttribute{ + Computed: true, + Description: "Provides information about existing GitCredentials.", + NestedObject: datasourceSchema.NestedAttributeObject{ + Attributes: GetGitCredentialAttributes(), + }, }, }, } @@ -65,17 +61,12 @@ func GetGitCredentialDataSourceSchema() map[string]datasourceSchema.Attribute { func GetGitCredentialAttributes() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ - "id": util.GetIdDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema(GitCredentialResourceDescription), - "name": util.GetQueryNameDatasourceSchema(), - "description": util.GetDescriptionDatasourceSchema(GitCredentialResourceDescription), - "type": datasourceSchema.StringAttribute{ - Computed: true, - Description: "The Git credential authentication type.", - }, - "username": datasourceSchema.StringAttribute{ - Computed: true, - Description: "The username for the Git credential.", - }, + "id": util.DataSourceString().Computed().Description("The unique ID for this resource.").Build(), + "space_id": util.DataSourceString().Computed().Description("The space ID associated with this Git Credential.").Build(), + "name": util.DataSourceString().Computed().Description("The name of this Git Credential.").Build(), + "description": util.DataSourceString().Computed().Description("The description of this Git Credential.").Build(), + "type": util.DataSourceString().Computed().Description("The Git credential authentication type.").Build(), + "username": util.DataSourceString().Computed().Description("The username for the Git credential.").Build(), + "password": util.DataSourceString().Computed().Sensitive().Description("The password for the Git credential.").Build(), } } diff --git a/octopusdeploy_framework/schemas/lifecycle.go b/octopusdeploy_framework/schemas/lifecycle.go index 04f7f493a..ba28f35b9 100644 --- a/octopusdeploy_framework/schemas/lifecycle.go +++ b/octopusdeploy_framework/schemas/lifecycle.go @@ -7,16 +7,18 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" ) func GetResourceLifecycleSchema() resourceSchema.Schema { return resourceSchema.Schema{ + Description: "This resource manages lifecycles in Octopus Deploy.", Attributes: map[string]resourceSchema.Attribute{ - "id": util.GetIdResourceSchema(), - "space_id": util.GetSpaceIdResourceSchema("lifecycle"), - "name": util.GetNameResourceSchema(true), - "description": util.GetDescriptionResourceSchema("lifecycle"), + "id": util.ResourceString().Optional().Computed().Description("The unique ID for this resource.").PlanModifiers(stringplanmodifier.UseStateForUnknown()).Build(), + "space_id": util.ResourceString().Optional().Computed().Description("The space ID associated with this resource.").PlanModifiers(stringplanmodifier.UseStateForUnknown()).Build(), + "name": util.ResourceString().Required().Description("The name of this resource.").Build(), + "description": util.ResourceString().Optional().Computed().Default("").Description("The description of this lifecycle.").Build(), }, Blocks: map[string]resourceSchema.Block{ "phase": getResourcePhaseBlockSchema(), @@ -28,30 +30,29 @@ func GetResourceLifecycleSchema() resourceSchema.Schema { func getResourcePhaseBlockSchema() resourceSchema.ListNestedBlock { return resourceSchema.ListNestedBlock{ + Description: "Defines a phase in the lifecycle.", NestedObject: resourceSchema.NestedBlockObject{ Attributes: map[string]resourceSchema.Attribute{ - "id": util.GetIdResourceSchema(), - "name": util.GetNameResourceSchema(true), - "automatic_deployment_targets": resourceSchema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - Computed: true, - }, - "optional_deployment_targets": resourceSchema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - Computed: true, - }, - "minimum_environments_before_promotion": resourceSchema.Int64Attribute{ - Optional: true, - Computed: true, - Default: int64default.StaticInt64(0), - }, - "is_optional_phase": resourceSchema.BoolAttribute{ - Optional: true, - Computed: true, - Default: booldefault.StaticBool(false), - }, + "id": util.ResourceString().Optional().Computed().Description("The unique ID for this resource.").Build(), + "name": util.ResourceString().Required().Description("The name of this resource.").Build(), + "automatic_deployment_targets": util.ResourceList(types.StringType). + Optional().Computed(). + Description("Environment IDs in this phase that a release is automatically deployed to when it is eligible for this phase"). + Build(), + "optional_deployment_targets": util.ResourceList(types.StringType). + Optional().Computed(). + Description("Environment IDs in this phase that a release can be deployed to, but is not automatically deployed to"). + Build(), + "minimum_environments_before_promotion": util.ResourceInt64(). + Optional().Computed(). + Default(int64default.StaticInt64(0)). + Description("The number of units required before a release can enter the next phase. If 0, all environments are required."). + Build(), + "is_optional_phase": util.ResourceBool(). + Optional().Computed(). + Default(booldefault.StaticBool(false)). + Description("If false a release must be deployed to this phase before it can be deployed to the next phase."). + Build(), }, Blocks: map[string]resourceSchema.Block{ "release_retention_policy": getResourceRetentionPolicyBlockSchema(), @@ -63,83 +64,87 @@ func getResourcePhaseBlockSchema() resourceSchema.ListNestedBlock { func getResourceRetentionPolicyBlockSchema() resourceSchema.ListNestedBlock { return resourceSchema.ListNestedBlock{ + Description: "Defines the retention policy for releases or tentacles.", NestedObject: resourceSchema.NestedBlockObject{ Attributes: map[string]resourceSchema.Attribute{ - "quantity_to_keep": resourceSchema.Int64Attribute{ - Optional: true, - Computed: true, - Default: int64default.StaticInt64(30), - }, - "should_keep_forever": resourceSchema.BoolAttribute{ - Optional: true, - Computed: true, - Default: booldefault.StaticBool(false), - }, - "unit": resourceSchema.StringAttribute{ - Optional: true, - Computed: true, - Default: stringdefault.StaticString("Days"), - }, + "quantity_to_keep": util.ResourceInt64(). + Optional().Computed(). + Default(int64default.StaticInt64(30)). + Description("The number of days/releases to keep. The default value is 30. If 0 then all are kept."). + Build(), + "should_keep_forever": util.ResourceBool(). + Optional().Computed(). + Default(booldefault.StaticBool(false)). + Description("Indicates if items should never be deleted. The default value is false."). + Build(), + "unit": util.ResourceString(). + Optional().Computed(). + Default(stringdefault.StaticString("Days")). + Description("The unit of quantity to keep. Valid units are Days or Items. The default value is Days."). + Build(), }, }, } } func GetDatasourceLifecycleSchema() datasourceSchema.Schema { - description := "lifecycle" return datasourceSchema.Schema{ + Description: "Provides information about existing lifecycles.", Attributes: map[string]datasourceSchema.Attribute{ - "id": util.GetIdDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema(description), - "ids": util.GetQueryIDsDatasourceSchema(), - "partial_name": util.GetQueryPartialNameDatasourceSchema(), - "skip": util.GetQuerySkipDatasourceSchema(), - "take": util.GetQueryTakeDatasourceSchema(), - "lifecycles": datasourceSchema.ListNestedAttribute{ - Computed: true, - Optional: false, - NestedObject: datasourceSchema.NestedAttributeObject{ - Attributes: map[string]datasourceSchema.Attribute{ - "id": util.GetIdDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema(description), - "name": util.GetNameDatasourceSchema(true), - "description": util.GetDescriptionDatasourceSchema(description), - "phase": getDatasourcePhasesSchema(), - "release_retention_policy": getDatasourceRetentionPolicySchema(), - "tentacle_retention_policy": getDatasourceRetentionPolicySchema(), - }, - }, + "id": util.DataSourceString().Computed().Description("The ID of the lifecycle.").Build(), + "space_id": util.DataSourceString().Optional().Description("The space ID associated with this lifecycle.").Build(), + "ids": util.DataSourceList(types.StringType).Optional().Description("A list of lifecycle IDs to filter by.").Build(), + "partial_name": util.DataSourceString().Optional().Description("A partial name to filter lifecycles by.").Build(), + "skip": util.DataSourceInt64().Optional().Description("A filter to specify the number of items to skip in the response.").Build(), + "take": util.DataSourceInt64().Optional().Description("A filter to specify the number of items to take (or return) in the response.").Build(), + "lifecycles": getLifecyclesAttribute(), + }, + } +} + +func getLifecyclesAttribute() datasourceSchema.ListNestedAttribute { + return datasourceSchema.ListNestedAttribute{ + Computed: true, + NestedObject: datasourceSchema.NestedAttributeObject{ + Attributes: map[string]datasourceSchema.Attribute{ + "id": util.DataSourceString().Computed().Description("The ID of the lifecycle.").Build(), + "space_id": util.DataSourceString().Computed().Description("The space ID associated with this lifecycle.").Build(), + "name": util.DataSourceString().Computed().Description("The name of the lifecycle.").Build(), + "description": util.DataSourceString().Computed().Description("The description of the lifecycle.").Build(), + "phase": getPhasesAttribute(), + "release_retention_policy": getRetentionPolicyAttribute(), + "tentacle_retention_policy": getRetentionPolicyAttribute(), }, }, } } -func getDatasourcePhasesSchema() datasourceSchema.ListNestedAttribute { +func getPhasesAttribute() datasourceSchema.ListNestedAttribute { return datasourceSchema.ListNestedAttribute{ Computed: true, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ - "id": util.GetIdDatasourceSchema(), - "name": util.GetNameDatasourceSchema(true), - "automatic_deployment_targets": datasourceSchema.ListAttribute{ElementType: types.StringType, Computed: true}, - "optional_deployment_targets": datasourceSchema.ListAttribute{ElementType: types.StringType, Computed: true}, - "minimum_environments_before_promotion": datasourceSchema.Int64Attribute{Computed: true}, - "is_optional_phase": datasourceSchema.BoolAttribute{Computed: true}, - "release_retention_policy": getDatasourceRetentionPolicySchema(), - "tentacle_retention_policy": getDatasourceRetentionPolicySchema(), + "id": util.DataSourceString().Computed().Description("The ID of the phase.").Build(), + "name": util.DataSourceString().Computed().Description("The name of the phase.").Build(), + "automatic_deployment_targets": util.DataSourceList(types.StringType).Computed().Description("The automatic deployment targets for this phase.").Build(), + "optional_deployment_targets": util.DataSourceList(types.StringType).Computed().Description("The optional deployment targets for this phase.").Build(), + "minimum_environments_before_promotion": util.DataSourceInt64().Computed().Description("The minimum number of environments before promotion.").Build(), + "is_optional_phase": util.DataSourceBool().Computed().Description("Whether this phase is optional.").Build(), + "release_retention_policy": getRetentionPolicyAttribute(), + "tentacle_retention_policy": getRetentionPolicyAttribute(), }, }, } } -func getDatasourceRetentionPolicySchema() datasourceSchema.ListNestedAttribute { +func getRetentionPolicyAttribute() datasourceSchema.ListNestedAttribute { return datasourceSchema.ListNestedAttribute{ Computed: true, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ - "quantity_to_keep": datasourceSchema.Int64Attribute{Computed: true}, - "should_keep_forever": datasourceSchema.BoolAttribute{Computed: true}, - "unit": datasourceSchema.StringAttribute{Computed: true}, + "quantity_to_keep": util.DataSourceInt64().Computed().Description("The quantity of releases to keep.").Build(), + "should_keep_forever": util.DataSourceBool().Computed().Description("Whether releases should be kept forever.").Build(), + "unit": util.DataSourceString().Computed().Description("The unit of time for the retention policy.").Build(), }, }, } diff --git a/octopusdeploy_framework/schemas/tenant_project_variable.go b/octopusdeploy_framework/schemas/tenant_project_variable.go index 99553e7c6..4e681a99e 100644 --- a/octopusdeploy_framework/schemas/tenant_project_variable.go +++ b/octopusdeploy_framework/schemas/tenant_project_variable.go @@ -3,6 +3,7 @@ package schemas import ( "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" ) const ( @@ -14,17 +15,38 @@ func GetTenantProjectVariableResourceSchema() schema.Schema { return schema.Schema{ Description: "Manages a tenant project variable in Octopus Deploy.", Attributes: map[string]schema.Attribute{ - "id": util.GetIdResourceSchema(), - "space_id": util.GetSpaceIdResourceSchema(TenantProjectVariableResourceDescription), - "tenant_id": util.GetRequiredStringResourceSchema("The ID of the tenant."), - "project_id": util.GetRequiredStringResourceSchema("The ID of the project."), - "environment_id": util.GetRequiredStringResourceSchema("The ID of the environment."), - "template_id": util.GetRequiredStringResourceSchema("The ID of the variable template."), - "value": schema.StringAttribute{ - Required: true, - Description: "The value of the variable.", - Sensitive: true, - }, + "id": util.ResourceString(). + Computed(). + Description("The unique ID for this resource."). + PlanModifiers(stringplanmodifier.UseStateForUnknown()). + Build(), + "space_id": util.ResourceString(). + Optional(). + Computed(). + Description("The space ID associated with this Tenant Project Variable."). + PlanModifiers(stringplanmodifier.UseStateForUnknown()). + Build(), + "tenant_id": util.ResourceString(). + Required(). + Description("The ID of the tenant."). + Build(), + "project_id": util.ResourceString(). + Required(). + Description("The ID of the project."). + Build(), + "environment_id": util.ResourceString(). + Required(). + Description("The ID of the environment."). + Build(), + "template_id": util.ResourceString(). + Required(). + Description("The ID of the variable template."). + Build(), + "value": util.ResourceString(). + Optional(). + Sensitive(). + Description("The value of the variable."). + Build(), }, } } diff --git a/octopusdeploy_framework/util/resource_attribute_builder.go b/octopusdeploy_framework/util/resource_attribute_builder.go index 153cafbf7..908f07929 100644 --- a/octopusdeploy_framework/util/resource_attribute_builder.go +++ b/octopusdeploy_framework/util/resource_attribute_builder.go @@ -8,8 +8,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/mapdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -217,6 +219,84 @@ func (b *AttributeBuilder[T]) Build() T { return b.attr } +func (b *AttributeBuilder[T]) PlanModifiers(modifiers ...any) *AttributeBuilder[T] { + switch a := any(&b.attr).(type) { + case *schema.StringAttribute: + if stringModifiers, ok := convertToTypedSlice[planmodifier.String](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, stringModifiers...) + } + case *schema.BoolAttribute: + if boolModifiers, ok := convertToTypedSlice[planmodifier.Bool](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, boolModifiers...) + } + case *schema.Int64Attribute: + if int64Modifiers, ok := convertToTypedSlice[planmodifier.Int64](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, int64Modifiers...) + } + case *schema.Float64Attribute: + if float64Modifiers, ok := convertToTypedSlice[planmodifier.Float64](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, float64Modifiers...) + } + case *schema.ListAttribute: + if listModifiers, ok := convertToTypedSlice[planmodifier.List](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, listModifiers...) + } + case *schema.SetAttribute: + if setModifiers, ok := convertToTypedSlice[planmodifier.Set](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, setModifiers...) + } + case *schema.MapAttribute: + if mapModifiers, ok := convertToTypedSlice[planmodifier.Map](modifiers); ok { + a.PlanModifiers = append(a.PlanModifiers, mapModifiers...) + } + } + return b +} +func (b *AttributeBuilder[T]) Validators(validators ...any) *AttributeBuilder[T] { + switch a := any(&b.attr).(type) { + case *schema.StringAttribute: + if stringValidators, ok := convertToTypedSlice[validator.String](validators); ok { + a.Validators = append(a.Validators, stringValidators...) + } + case *schema.BoolAttribute: + if boolValidators, ok := convertToTypedSlice[validator.Bool](validators); ok { + a.Validators = append(a.Validators, boolValidators...) + } + case *schema.Int64Attribute: + if int64Validators, ok := convertToTypedSlice[validator.Int64](validators); ok { + a.Validators = append(a.Validators, int64Validators...) + } + case *schema.Float64Attribute: + if float64Validators, ok := convertToTypedSlice[validator.Float64](validators); ok { + a.Validators = append(a.Validators, float64Validators...) + } + case *schema.ListAttribute: + if listValidators, ok := convertToTypedSlice[validator.List](validators); ok { + a.Validators = append(a.Validators, listValidators...) + } + case *schema.SetAttribute: + if setValidators, ok := convertToTypedSlice[validator.Set](validators); ok { + a.Validators = append(a.Validators, setValidators...) + } + case *schema.MapAttribute: + if mapValidators, ok := convertToTypedSlice[validator.Map](validators); ok { + a.Validators = append(a.Validators, mapValidators...) + } + } + return b +} + +func convertToTypedSlice[T any](slice []any) ([]T, bool) { + typedSlice := make([]T, 0, len(slice)) + for _, item := range slice { + if typed, ok := item.(T); ok { + typedSlice = append(typedSlice, typed) + } else { + return nil, false + } + } + return typedSlice, true +} func ResourceString() *AttributeBuilder[schema.StringAttribute] { return NewAttributeBuilder[schema.StringAttribute]() }