From fdcf66c8ed14ec4394d8889c1b820caa89648fe8 Mon Sep 17 00:00:00 2001 From: Isaac Calligeros <101079287+IsaacCalligeros95@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:34:53 +0930 Subject: [PATCH 01/12] Chore!: Migrate docker container registry (#702) * Migrate docker container registry * Tidy up * Rename APIVersion * Fix descriptions --- docs/data-sources/feeds.md | 35 ++-- docs/data-sources/library_variable_sets.md | 4 +- docs/resources/artifactory_generic_feed.md | 4 +- .../aws_elastic_container_registry.md | 6 +- docs/resources/docker_container_registry.md | 6 +- docs/resources/github_repository_feed.md | 4 +- docs/resources/helm_feed.md | 8 +- docs/resources/library_variable_set.md | 4 +- docs/resources/maven_feed.md | 4 +- docs/resources/nuget_feed.md | 6 +- octopusdeploy/provider.go | 1 - .../resource_docker_container_registry.go | 104 ---------- .../schema_docker_container_registry.go | 104 ---------- octopusdeploy_framework/framework_provider.go | 3 +- .../resource_artifactory_generic_feed.go | 3 +- ...resource_aws_elastic_container_registry.go | 3 +- .../resource_docker_container_registry.go | 183 ++++++++++++++++++ ...resource_docker_container_registry_test.go | 11 +- .../resource_github_repository_feed.go | 3 +- octopusdeploy_framework/resource_helm_feed.go | 3 +- .../resource_maven_feed.go | 3 +- .../resource_nuget_feed.go | 3 +- .../schemas/docker_container_registry_feed.go | 39 ++++ .../schemas/library_variable_set.go | 4 +- 24 files changed, 286 insertions(+), 262 deletions(-) delete mode 100644 octopusdeploy/resource_docker_container_registry.go delete mode 100644 octopusdeploy/schema_docker_container_registry.go create mode 100644 octopusdeploy_framework/resource_docker_container_registry.go rename {octopusdeploy => octopusdeploy_framework}/resource_docker_container_registry_test.go (94%) create mode 100644 octopusdeploy_framework/schemas/docker_container_registry_feed.go diff --git a/docs/data-sources/feeds.md b/docs/data-sources/feeds.md index 623ec5593..91fde3c96 100644 --- a/docs/data-sources/feeds.md +++ b/docs/data-sources/feeds.md @@ -27,37 +27,40 @@ data "octopusdeploy_feeds" "example" { ### Optional - `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`. +- `feeds` (Block List) (see [below for nested schema](#nestedblock--feeds)) +- `id` (String) The unique ID for this resource. - `ids` (List of String) A filter to search by a list of IDs. -- `name` (String) A filter to search by name. -- `partial_name` (String) A filter to search by the partial match of a name. +- `name` (String) The name of this resource. +- `partial_name` (String) A filter to search by a partial name. - `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 feeds. - `take` (Number) A filter to specify the number of items to take (or return) in the response. -### Read-Only - -- `feeds` (Block List) A list of feeds that match the filter(s). (see [below for nested schema](#nestedblock--feeds)) -- `id` (String) The ID of this resource. - ### Nested Schema for `feeds` -Read-Only: +Required: + +- `access_key` (String) The AWS access key to use when authenticating against Amazon Web Services. +- `feed_uri` (String) +- `name` (String) The name of this resource. + +Optional: -- `access_key` (String) - `api_version` (String) - `delete_unreleased_packages_after_days` (Number) - `download_attempts` (Number) The number of times a deployment should attempt to download a package from this feed before failing. - `download_retry_backoff_seconds` (Number) The number of seconds to apply as a linear back off between download attempts. -- `feed_type` (String) -- `feed_uri` (String) +- `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`. - `id` (String) The unique ID for this resource. - `is_enhanced_mode` (Boolean) -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. -- `region` (String) - `registry_path` (String) - `secret_key` (String, Sensitive) -- `space_id` (String) The space ID associated with this resource. -- `username` (String, Sensitive) The username associated with this resource. \ No newline at end of file +- `space_id` (String) The space ID associated with this feeds. +- `username` (String, Sensitive) The username associated with this resource. + +Read-Only: + +- `region` (String) \ No newline at end of file diff --git a/docs/data-sources/library_variable_sets.md b/docs/data-sources/library_variable_sets.md index da636ab68..8b6eeb19f 100644 --- a/docs/data-sources/library_variable_sets.md +++ b/docs/data-sources/library_variable_sets.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_library_variable_sets Data Source - terraform-provider-octopusdeploy" subcategory: "" description: |- - + Provides information about existing library variable sets. --- # octopusdeploy_library_variable_sets (Data Source) - +Provides information about existing library variable sets. diff --git a/docs/resources/artifactory_generic_feed.md b/docs/resources/artifactory_generic_feed.md index 3d46c6eec..07ea676ce 100644 --- a/docs/resources/artifactory_generic_feed.md +++ b/docs/resources/artifactory_generic_feed.md @@ -29,7 +29,7 @@ resource "octopusdeploy_artifactory_generic_feed" "example" { ### Required - `feed_uri` (String) -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `name` (String) The name of this resource. - `repository` (String) ### Optional @@ -38,7 +38,7 @@ resource "octopusdeploy_artifactory_generic_feed" "example" { - `layout_regex` (String) - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this helm feed. - `username` (String, Sensitive) The username associated with this resource. ## Import diff --git a/docs/resources/aws_elastic_container_registry.md b/docs/resources/aws_elastic_container_registry.md index 5549897ea..e0b4f802f 100644 --- a/docs/resources/aws_elastic_container_registry.md +++ b/docs/resources/aws_elastic_container_registry.md @@ -25,15 +25,15 @@ resource "octopusdeploy_aws_elastic_container_registry" "example" { ### Required - `access_key` (String) The AWS access key to use when authenticating against Amazon Web Services. -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `name` (String) The name of this resource. - `region` (String) The AWS region where the registry resides. - `secret_key` (String, Sensitive) The AWS secret key to use when authenticating against Amazon Web Services. ### Optional -- `id` (String) The unique ID for this feed. +- `id` (String) The unique ID for this resource. - `package_acquisition_location_options` (List of String) -- `space_id` (String) The space ID associated with this feed. +- `space_id` (String) The space ID associated with this aws elastic container registry. ## Import diff --git a/docs/resources/docker_container_registry.md b/docs/resources/docker_container_registry.md index ebc6b68cb..8a59fe071 100644 --- a/docs/resources/docker_container_registry.md +++ b/docs/resources/docker_container_registry.md @@ -25,8 +25,8 @@ resource "octopusdeploy_docker_container_registry" "example" { ### Required -- `feed_uri` (String) The URL to a Docker repository. -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `feed_uri` (String) +- `name` (String) The name of this resource. ### Optional @@ -35,7 +35,7 @@ resource "octopusdeploy_docker_container_registry" "example" { - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. - `registry_path` (String) -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this docker container registry feed. - `username` (String, Sensitive) The username associated with this resource. ## Import diff --git a/docs/resources/github_repository_feed.md b/docs/resources/github_repository_feed.md index c743a47dd..37e9601c1 100644 --- a/docs/resources/github_repository_feed.md +++ b/docs/resources/github_repository_feed.md @@ -27,7 +27,7 @@ resource "octopusdeploy_github_repository_feed" "example" { ### Required - `feed_uri` (String) -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `name` (String) The name of this resource. ### Optional @@ -36,7 +36,7 @@ resource "octopusdeploy_github_repository_feed" "example" { - `id` (String) The unique ID for this resource. - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this github repository feed. - `username` (String, Sensitive) The username associated with this resource. ## Import diff --git a/docs/resources/helm_feed.md b/docs/resources/helm_feed.md index 037bc5888..5eed44882 100644 --- a/docs/resources/helm_feed.md +++ b/docs/resources/helm_feed.md @@ -2,12 +2,12 @@ page_title: "octopusdeploy_helm_feed Resource - terraform-provider-octopusdeploy" subcategory: "Feeds" description: |- - This resource manages a Helm feed in Octopus Deploy. + This resource manages a Helm Feed in Octopus Deploy. --- # octopusdeploy_helm_feed (Resource) -This resource manages a Helm feed in Octopus Deploy. +This resource manages a Helm Feed in Octopus Deploy. ## Example Usage @@ -25,14 +25,14 @@ resource "octopusdeploy_helm_feed" "example" { ### Required - `feed_uri` (String) -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `name` (String) The name of this resource. ### Optional - `id` (String) The unique ID for this resource. - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this helm feed. - `username` (String, Sensitive) The username associated with this resource. ## Import diff --git a/docs/resources/library_variable_set.md b/docs/resources/library_variable_set.md index b30fd3f1f..9bff4bf78 100644 --- a/docs/resources/library_variable_set.md +++ b/docs/resources/library_variable_set.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_library_variable_set Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - + This resource manages library variable sets in Octopus Deploy. --- # octopusdeploy_library_variable_set (Resource) - +This resource manages library variable sets in Octopus Deploy. diff --git a/docs/resources/maven_feed.md b/docs/resources/maven_feed.md index a589dc13f..1c01d16e8 100644 --- a/docs/resources/maven_feed.md +++ b/docs/resources/maven_feed.md @@ -27,7 +27,7 @@ resource "octopusdeploy_maven_feed" "example" { ### Required - `feed_uri` (String) -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `name` (String) The name of this resource. ### Optional @@ -36,7 +36,7 @@ resource "octopusdeploy_maven_feed" "example" { - `id` (String) The unique ID for this resource. - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this maven feed. - `username` (String, Sensitive) The username associated with this resource. ## Import diff --git a/docs/resources/nuget_feed.md b/docs/resources/nuget_feed.md index 0444d55ba..b5758af66 100644 --- a/docs/resources/nuget_feed.md +++ b/docs/resources/nuget_feed.md @@ -27,8 +27,8 @@ resource "octopusdeploy_nuget_feed" "example" { ### Required -- `feed_uri` (String) The feed URI can be a URL or a folder path. -- `name` (String) A short, memorable, unique name for this feed. Example: ACME Builds. +- `feed_uri` (String) +- `name` (String) The name of this resource. ### Optional @@ -38,7 +38,7 @@ resource "octopusdeploy_nuget_feed" "example" { - `is_enhanced_mode` (Boolean) This will improve performance of the NuGet feed but may not be supported by some older feeds. Disable if the operation, Create Release does not return the latest version for a package. - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this nuget feed. - `username` (String, Sensitive) The username associated with this resource. ## Import diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index cd51d9521..07f503374 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -48,7 +48,6 @@ func Provider() *schema.Provider { "octopusdeploy_channel": resourceChannel(), "octopusdeploy_cloud_region_deployment_target": resourceCloudRegionDeploymentTarget(), "octopusdeploy_deployment_process": resourceDeploymentProcess(), - "octopusdeploy_docker_container_registry": resourceDockerContainerRegistry(), "octopusdeploy_dynamic_worker_pool": resourceDynamicWorkerPool(), "octopusdeploy_gcp_account": resourceGoogleCloudPlatformAccount(), "octopusdeploy_kubernetes_agent_deployment_target": resourceKubernetesAgentDeploymentTarget(), diff --git a/octopusdeploy/resource_docker_container_registry.go b/octopusdeploy/resource_docker_container_registry.go deleted file mode 100644 index bf5d1edd8..000000000 --- a/octopusdeploy/resource_docker_container_registry.go +++ /dev/null @@ -1,104 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceDockerContainerRegistry() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceDockerContainerRegistryCreate, - DeleteContext: resourceDockerContainerRegistryDelete, - Description: "This resource manages a Docker Container Registry in Octopus Deploy.", - Importer: getImporter(), - ReadContext: resourceDockerContainerRegistryRead, - Schema: getDockerContainerRegistrySchema(), - UpdateContext: resourceDockerContainerRegistryUpdate, - } -} - -func resourceDockerContainerRegistryCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - dockerContainerRegistry, err := expandDockerContainerRegistry(d) - if err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("creating Docker container registry, %s", dockerContainerRegistry.GetName())) - - client := m.(*client.Client) - createdDockerContainerRegistry, err := feeds.Add(client, dockerContainerRegistry) - if err != nil { - return diag.FromErr(err) - } - - if err := setDockerContainerRegistry(ctx, d, createdDockerContainerRegistry.(*feeds.DockerContainerRegistry)); err != nil { - return diag.FromErr(err) - } - - d.SetId(createdDockerContainerRegistry.GetID()) - - tflog.Info(ctx, fmt.Sprintf("Docker container registry created (%s)", d.Id())) - return nil -} - -func resourceDockerContainerRegistryDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, fmt.Sprintf("deleting Docker container registry (%s)", d.Id())) - - client := m.(*client.Client) - err := client.Feeds.DeleteByID(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - d.SetId("") - - tflog.Info(ctx, "Docker container registry deleted") - return nil -} - -func resourceDockerContainerRegistryRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, fmt.Sprintf("reading Docker container registry (%s)", d.Id())) - - client := m.(*client.Client) - feed, err := feeds.GetByID(client, d.Get("space_id").(string), d.Id()) - if err != nil { - return errors.ProcessApiError(ctx, d, err, "Docker container registry") - } - - dockerContainerRegistry := feed.(*feeds.DockerContainerRegistry) - if err := setDockerContainerRegistry(ctx, d, dockerContainerRegistry); err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("Docker container registry read (%s)", dockerContainerRegistry.GetID())) - return nil -} - -func resourceDockerContainerRegistryUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - feed, err := expandDockerContainerRegistry(d) - if err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("updating Docker container registry (%s)", feed.GetID())) - - client := m.(*client.Client) - updatedFeed, err := feeds.Update(client, feed) - if err != nil { - return diag.FromErr(err) - } - - if err := setDockerContainerRegistry(ctx, d, updatedFeed.(*feeds.DockerContainerRegistry)); err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("Docker container registry updated (%s)", d.Id())) - return nil -} diff --git a/octopusdeploy/schema_docker_container_registry.go b/octopusdeploy/schema_docker_container_registry.go deleted file mode 100644 index a28f565cd..000000000 --- a/octopusdeploy/schema_docker_container_registry.go +++ /dev/null @@ -1,104 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func expandDockerContainerRegistry(d *schema.ResourceData) (*feeds.DockerContainerRegistry, error) { - name := d.Get("name").(string) - - feed, err := feeds.NewDockerContainerRegistry(name) - if err != nil { - return nil, err - } - - feed.ID = d.Id() - - if v, ok := d.GetOk("api_version"); ok { - feed.APIVersion = v.(string) - } - - if v, ok := d.GetOk("feed_uri"); ok { - feed.FeedURI = v.(string) - } - - if v, ok := d.GetOk("registry_path"); ok { - feed.RegistryPath = v.(string) - } - - if v, ok := d.GetOk("space_id"); ok { - feed.SpaceID = v.(string) - } - - if v, ok := d.GetOk("package_acquisition_location_options"); ok { - feed.PackageAcquisitionLocationOptions = getSliceFromTerraformTypeList(v) - } - - if v, ok := d.GetOk("password"); ok { - feed.Password = core.NewSensitiveValue(v.(string)) - } - - if v, ok := d.GetOk("username"); ok { - feed.Username = v.(string) - } - - return feed, nil -} - -func getDockerContainerRegistrySchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "api_version": { - Optional: true, - Type: schema.TypeString, - }, - "feed_uri": { - Description: "The URL to a Docker repository.", - Required: true, - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.IsURLWithHTTPorHTTPS), - }, - "id": getIDSchema(), - "name": { - Description: "A short, memorable, unique name for this feed. Example: ACME Builds.", - Required: true, - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotEmpty), - }, - "password": getPasswordSchema(false), - "package_acquisition_location_options": { - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Type: schema.TypeList, - }, - "registry_path": { - Optional: true, - Type: schema.TypeString, - }, - "space_id": getSpaceIDSchema(), - "username": getUsernameSchema(false), - } -} - -func setDockerContainerRegistry(ctx context.Context, d *schema.ResourceData, feed *feeds.DockerContainerRegistry) error { - d.Set("api_version", feed.APIVersion) - d.Set("feed_uri", feed.FeedURI) - d.Set("name", feed.Name) - d.Set("registry_path", feed.RegistryPath) - d.Set("space_id", feed.SpaceID) - d.Set("username", feed.Username) - - if err := d.Set("package_acquisition_location_options", feed.PackageAcquisitionLocationOptions); err != nil { - return fmt.Errorf("error setting package_acquisition_location_options: %s", err) - } - - d.SetId(feed.GetID()) - - return nil -} diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index 816b8f774..447f66237 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -2,9 +2,9 @@ package octopusdeploy_framework import ( "context" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "os" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" @@ -92,6 +92,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func() NewLibraryVariableSetFeedResource, NewVariableResource, NewProjectResource, + NewDockerContainerRegistryFeedResource, } } diff --git a/octopusdeploy_framework/resource_artifactory_generic_feed.go b/octopusdeploy_framework/resource_artifactory_generic_feed.go index e70900061..a1eb7ab58 100644 --- a/octopusdeploy_framework/resource_artifactory_generic_feed.go +++ b/octopusdeploy_framework/resource_artifactory_generic_feed.go @@ -29,7 +29,8 @@ func (r *artifactoryGenericFeedTypeResource) Metadata(ctx context.Context, req r func (r *artifactoryGenericFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: schemas.GetArtifactoryGenericFeedResourceSchema(), + Attributes: schemas.GetArtifactoryGenericFeedResourceSchema(), + Description: "This resource manages a Artifactory Generic feed in Octopus Deploy.", } } diff --git a/octopusdeploy_framework/resource_aws_elastic_container_registry.go b/octopusdeploy_framework/resource_aws_elastic_container_registry.go index 96f089a83..72cef3b51 100644 --- a/octopusdeploy_framework/resource_aws_elastic_container_registry.go +++ b/octopusdeploy_framework/resource_aws_elastic_container_registry.go @@ -28,7 +28,8 @@ func (r *awsElasticContainerRegistryFeedTypeResource) Metadata(ctx context.Conte func (r *awsElasticContainerRegistryFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: schemas.GetAwsElasticContainerRegistryFeedResourceSchema(), + Attributes: schemas.GetAwsElasticContainerRegistryFeedResourceSchema(), + Description: "This resource manages an AWS Elastic Container Registry in Octopus Deploy.", } } diff --git a/octopusdeploy_framework/resource_docker_container_registry.go b/octopusdeploy_framework/resource_docker_container_registry.go new file mode 100644 index 000000000..fa73ef902 --- /dev/null +++ b/octopusdeploy_framework/resource_docker_container_registry.go @@ -0,0 +1,183 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +type dockerContainerRegistryFeedTypeResource struct { + *Config +} + +func NewDockerContainerRegistryFeedResource() resource.Resource { + return &dockerContainerRegistryFeedTypeResource{} +} + +func (r *dockerContainerRegistryFeedTypeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = util.GetTypeName("docker_container_registry") +} + +func (r *dockerContainerRegistryFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: schemas.GetDockerContainerRegistryFeedResourceSchema(), + Description: "This resource manages a Docker Container Registry in Octopus Deploy.", + } +} + +func (r *dockerContainerRegistryFeedTypeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.Config = ResourceConfiguration(req, resp) +} + +func (r *dockerContainerRegistryFeedTypeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data *schemas.DockerContainerRegistryFeedTypeResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + dockerContainerRegistryFeed, err := createDockerContainerRegistryFeedResourceFromData(data) + if err != nil { + return + } + + tflog.Info(ctx, fmt.Sprintf("creating Docker Container Registry feed: %s", dockerContainerRegistryFeed.GetName())) + + client := r.Config.Client + createdFeed, err := feeds.Add(client, dockerContainerRegistryFeed) + if err != nil { + resp.Diagnostics.AddError("unable to create docker container registry feed", err.Error()) + return + } + + updateDataFromDockerContainerRegistryFeed(data, data.SpaceID.ValueString(), createdFeed.(*feeds.DockerContainerRegistry)) + + tflog.Info(ctx, fmt.Sprintf("Docker Container Registry feed created (%s)", data.ID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *dockerContainerRegistryFeedTypeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *schemas.DockerContainerRegistryFeedTypeResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Info(ctx, fmt.Sprintf("reading Docker Container Registry feed (%s)", data.ID)) + + client := r.Config.Client + feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("unable to load docker container registry feed", err.Error()) + return + } + + dockerContainerRegistry := feed.(*feeds.DockerContainerRegistry) + updateDataFromDockerContainerRegistryFeed(data, data.SpaceID.ValueString(), dockerContainerRegistry) + + tflog.Info(ctx, fmt.Sprintf("Docker Container Registry feed read (%s)", dockerContainerRegistry.GetID())) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *dockerContainerRegistryFeedTypeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data, state *schemas.DockerContainerRegistryFeedTypeResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("updating docker container registry feed '%s'", data.ID.ValueString())) + + feed, err := createDockerContainerRegistryFeedResourceFromData(data) + feed.ID = state.ID.ValueString() + if err != nil { + resp.Diagnostics.AddError("unable to load docker container registry feed", err.Error()) + return + } + + tflog.Info(ctx, fmt.Sprintf("updating Docker Container Registry feed (%s)", data.ID)) + + client := r.Config.Client + updatedFeed, err := feeds.Update(client, feed) + if err != nil { + resp.Diagnostics.AddError("unable to update docker container registry feed", err.Error()) + return + } + + updateDataFromDockerContainerRegistryFeed(data, state.SpaceID.ValueString(), updatedFeed.(*feeds.DockerContainerRegistry)) + + tflog.Info(ctx, fmt.Sprintf("Docker Container Registry feed updated (%s)", data.ID)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *dockerContainerRegistryFeedTypeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data schemas.DockerContainerRegistryFeedTypeResourceModel + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if err := feeds.DeleteByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("unable to delete docker container registry feed", err.Error()) + return + } +} + +func createDockerContainerRegistryFeedResourceFromData(data *schemas.DockerContainerRegistryFeedTypeResourceModel) (*feeds.DockerContainerRegistry, error) { + feed, err := feeds.NewDockerContainerRegistry(data.Name.ValueString()) + if err != nil { + return nil, err + } + + feed.ID = data.ID.ValueString() + feed.FeedURI = data.FeedUri.ValueString() + + var packageAcquisitionLocationOptions []string + for _, element := range data.PackageAcquisitionLocationOptions.Elements() { + packageAcquisitionLocationOptions = append(packageAcquisitionLocationOptions, element.(types.String).ValueString()) + } + + feed.PackageAcquisitionLocationOptions = packageAcquisitionLocationOptions + feed.Password = core.NewSensitiveValue(data.Password.ValueString()) + feed.SpaceID = data.SpaceID.ValueString() + feed.Username = data.Username.ValueString() + feed.APIVersion = data.APIVersion.ValueString() + feed.RegistryPath = data.RegistryPath.ValueString() + + return feed, nil +} + +func updateDataFromDockerContainerRegistryFeed(data *schemas.DockerContainerRegistryFeedTypeResourceModel, spaceId string, feed *feeds.DockerContainerRegistry) { + data.FeedUri = types.StringValue(feed.FeedURI) + data.Name = types.StringValue(feed.Name) + data.SpaceID = types.StringValue(spaceId) + if feed.APIVersion != "" { + data.APIVersion = types.StringValue(feed.APIVersion) + } + if feed.RegistryPath != "" { + data.RegistryPath = types.StringValue(feed.RegistryPath) + } + if feed.Username != "" { + data.Username = types.StringValue(feed.Username) + } + + packageAcquisitionLocationOptionsList := make([]attr.Value, len(feed.PackageAcquisitionLocationOptions)) + for i, option := range feed.PackageAcquisitionLocationOptions { + packageAcquisitionLocationOptionsList[i] = types.StringValue(option) + } + + var packageAcquisitionLocationOptionsListValue, _ = types.ListValue(types.StringType, packageAcquisitionLocationOptionsList) + data.PackageAcquisitionLocationOptions = packageAcquisitionLocationOptionsListValue + data.ID = types.StringValue(feed.ID) +} diff --git a/octopusdeploy/resource_docker_container_registry_test.go b/octopusdeploy_framework/resource_docker_container_registry_test.go similarity index 94% rename from octopusdeploy/resource_docker_container_registry_test.go rename to octopusdeploy_framework/resource_docker_container_registry_test.go index 377ced443..9e2c893fa 100644 --- a/octopusdeploy/resource_docker_container_registry_test.go +++ b/octopusdeploy_framework/resource_docker_container_registry_test.go @@ -1,16 +1,15 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/octoclient" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/test" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "path/filepath" "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccOctopusDeployDockerContainerRegistry(t *testing.T) { @@ -26,7 +25,7 @@ func TestAccOctopusDeployDockerContainerRegistry(t *testing.T) { resource.Test(t, resource.TestCase{ CheckDestroy: testDockerContainerRegistryCheckDestroy, - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: ProtoV6ProviderFactories(), Steps: []resource.TestStep{ { diff --git a/octopusdeploy_framework/resource_github_repository_feed.go b/octopusdeploy_framework/resource_github_repository_feed.go index 7a198daaa..30f7bdb28 100644 --- a/octopusdeploy_framework/resource_github_repository_feed.go +++ b/octopusdeploy_framework/resource_github_repository_feed.go @@ -28,7 +28,8 @@ func (r *githubRepositoryFeedTypeResource) Metadata(ctx context.Context, req res func (r *githubRepositoryFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: schemas.GetGitHubRepositoryFeedResourceSchema(), + Attributes: schemas.GetGitHubRepositoryFeedResourceSchema(), + Description: "This resource manages a GitHub repository feed in Octopus Deploy.", } } diff --git a/octopusdeploy_framework/resource_helm_feed.go b/octopusdeploy_framework/resource_helm_feed.go index 4fc1c9d3c..64ec52677 100644 --- a/octopusdeploy_framework/resource_helm_feed.go +++ b/octopusdeploy_framework/resource_helm_feed.go @@ -29,7 +29,8 @@ func (r *helmFeedTypeResource) Metadata(ctx context.Context, req resource.Metada func (r *helmFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: schemas.GetHelmFeedResourceSchema(), + Attributes: schemas.GetHelmFeedResourceSchema(), + Description: "This resource manages a Helm Feed in Octopus Deploy.", } } diff --git a/octopusdeploy_framework/resource_maven_feed.go b/octopusdeploy_framework/resource_maven_feed.go index c6f6ddd19..722081e81 100644 --- a/octopusdeploy_framework/resource_maven_feed.go +++ b/octopusdeploy_framework/resource_maven_feed.go @@ -28,7 +28,8 @@ func (r *mavenFeedTypeResource) Metadata(ctx context.Context, req resource.Metad func (r *mavenFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: schemas.GetMavenFeedResourceSchema(), + Attributes: schemas.GetMavenFeedResourceSchema(), + Description: "This resource manages a Maven feed in Octopus Deploy.", } } diff --git a/octopusdeploy_framework/resource_nuget_feed.go b/octopusdeploy_framework/resource_nuget_feed.go index 306e16f14..f053ea450 100644 --- a/octopusdeploy_framework/resource_nuget_feed.go +++ b/octopusdeploy_framework/resource_nuget_feed.go @@ -28,7 +28,8 @@ func (r *nugetFeedTypeResource) Metadata(ctx context.Context, req resource.Metad func (r *nugetFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Attributes: schemas.GetNugetFeedResourceSchema(), + Attributes: schemas.GetNugetFeedResourceSchema(), + Description: "This resource manages a Nuget feed in Octopus Deploy.", } } diff --git a/octopusdeploy_framework/schemas/docker_container_registry_feed.go b/octopusdeploy_framework/schemas/docker_container_registry_feed.go new file mode 100644 index 000000000..d7ff550c4 --- /dev/null +++ b/octopusdeploy_framework/schemas/docker_container_registry_feed.go @@ -0,0 +1,39 @@ +package schemas + +import ( + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +const dockerContainerRegistryFeedDescription = "docker container registry feed" + +func GetDockerContainerRegistryFeedResourceSchema() map[string]resourceSchema.Attribute { + return map[string]resourceSchema.Attribute{ + "api_version": resourceSchema.StringAttribute{ + Optional: true, + }, + "feed_uri": util.GetFeedUriResourceSchema(), + "id": util.GetIdResourceSchema(), + "name": util.GetNameResourceSchema(true), + "package_acquisition_location_options": util.GetPackageAcquisitionLocationOptionsResourceSchema(), + "password": util.GetPasswordResourceSchema(false), + "space_id": util.GetSpaceIdResourceSchema(dockerContainerRegistryFeedDescription), + "username": util.GetUsernameResourceSchema(false), + "registry_path": resourceSchema.StringAttribute{ + Optional: true, + }, + } +} + +type DockerContainerRegistryFeedTypeResourceModel struct { + APIVersion types.String `tfsdk:"api_version"` + FeedUri types.String `tfsdk:"feed_uri"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` + Password types.String `tfsdk:"password"` + SpaceID types.String `tfsdk:"space_id"` + Username types.String `tfsdk:"username"` + RegistryPath types.String `tfsdk:"registry_path"` +} diff --git a/octopusdeploy_framework/schemas/library_variable_set.go b/octopusdeploy_framework/schemas/library_variable_set.go index f3355768f..5fab75d02 100644 --- a/octopusdeploy_framework/schemas/library_variable_set.go +++ b/octopusdeploy_framework/schemas/library_variable_set.go @@ -22,7 +22,8 @@ type LibraryVariableSetResourceModel struct { func GetLibraryVariableSetDataSourceSchema() datasourceSchema.Schema { return datasourceSchema.Schema{ - Attributes: getLibraryVariableSetDataSchema(), + Attributes: getLibraryVariableSetDataSchema(), + Description: "Provides information about existing library variable sets.", Blocks: map[string]datasourceSchema.Block{ "library_variable_sets": datasourceSchema.ListNestedBlock{ Description: "A list of library variable sets that match the filter(s).", @@ -97,6 +98,7 @@ func GetLibraryVariableSetResourceSchema() resourceSchema.Schema { Computed: true, }, }, + Description: "This resource manages library variable sets in Octopus Deploy.", Blocks: map[string]resourceSchema.Block{ "template": resourceSchema.ListNestedBlock{ NestedObject: resourceSchema.NestedBlockObject{ From 7c81ce944a04db0272c2a4aab8adac7d3b56a1b1 Mon Sep 17 00:00:00 2001 From: Huy Nguyen <162080607+HuyPhanNguyen@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:37:14 +1000 Subject: [PATCH 02/12] Fix bug datasource spaces (#710) * Fix query.Ids convert and add basic test * Update the test * Fix the util method instead * just ignore if cant convert to string * try to fix flaky test * second try * Add lifecycle datasource test * Last try to avoid race condition * Test why not 7 variables create * switch the order --- octopusdeploy/testing_container_test.go | 5 ++ .../datasource_lifecycle_test.go | 63 +++++++++++++++++ .../datasource_spaces_test.go | 58 ++++++++++++++++ .../resource_variable_test.go | 15 +++- octopusdeploy_framework/schemas/schema.go | 7 +- terraform/49-variables/variables.tf | 68 ++++++++++++++++--- 6 files changed, 203 insertions(+), 13 deletions(-) create mode 100644 octopusdeploy_framework/datasource_lifecycle_test.go create mode 100644 octopusdeploy_framework/datasource_spaces_test.go diff --git a/octopusdeploy/testing_container_test.go b/octopusdeploy/testing_container_test.go index 8c1bd6397..6fd5dfba7 100644 --- a/octopusdeploy/testing_container_test.go +++ b/octopusdeploy/testing_container_test.go @@ -57,6 +57,11 @@ func TestMain(m *testing.M) { log.Printf("Failed to create client: (%s)", err.Error()) panic(m) } + + octoContainer = &test.OctopusContainer{ + Container: nil, + URI: url, + } } code := m.Run() os.Exit(code) diff --git a/octopusdeploy_framework/datasource_lifecycle_test.go b/octopusdeploy_framework/datasource_lifecycle_test.go new file mode 100644 index 000000000..c7febe404 --- /dev/null +++ b/octopusdeploy_framework/datasource_lifecycle_test.go @@ -0,0 +1,63 @@ +package octopusdeploy_framework + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDataSourceLifecycles(t *testing.T) { + spaceName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + lifecycleName := "Default Lifecycle" + resourceName := "data.octopusdeploy_lifecycles.lifecycle_default_lifecycle" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { TestAccPreCheck(t) }, + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceLifecyclesConfig(spaceName, lifecycleName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "space_id"), + resource.TestCheckResourceAttr(resourceName, "partial_name", lifecycleName), + resource.TestCheckResourceAttr(resourceName, "lifecycles.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "lifecycles.0.id"), + resource.TestCheckResourceAttr(resourceName, "lifecycles.0.name", lifecycleName), + testAccCheckOutputExists("octopus_space_id"), + testAccCheckOutputExists("octopus_lifecycle_id"), + ), + }, + }, + }) +} + +func testAccDataSourceLifecyclesConfig(spaceName, lifecycleName string) string { + return fmt.Sprintf(` +resource "octopusdeploy_space" "octopus_project_space_test" { + name = "%s" + is_default = false + is_task_queue_stopped = false + description = "Test space for lifecycles datasource" + space_managers_teams = ["teams-administrators"] +} + +data "octopusdeploy_lifecycles" "lifecycle_default_lifecycle" { + ids = null + partial_name = "%s" + space_id = octopusdeploy_space.octopus_project_space_test.id + skip = 0 + take = 1 + depends_on = [octopusdeploy_space.octopus_project_space_test] +} + +output "octopus_space_id" { + value = octopusdeploy_space.octopus_project_space_test.id +} + +output "octopus_lifecycle_id" { + value = data.octopusdeploy_lifecycles.lifecycle_default_lifecycle.lifecycles[0].id +} +`, spaceName, lifecycleName) +} diff --git a/octopusdeploy_framework/datasource_spaces_test.go b/octopusdeploy_framework/datasource_spaces_test.go new file mode 100644 index 000000000..dc48f1108 --- /dev/null +++ b/octopusdeploy_framework/datasource_spaces_test.go @@ -0,0 +1,58 @@ +package octopusdeploy_framework + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccDataSourceSpaces(t *testing.T) { + spaceID := "Spaces-1" + resourceName := "data.octopusdeploy_spaces.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { TestAccPreCheck(t) }, + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSpacesConfig(spaceID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "ids.0", spaceID), + resource.TestCheckResourceAttr(resourceName, "skip", "0"), + resource.TestCheckResourceAttr(resourceName, "take", "1"), + resource.TestCheckResourceAttrSet(resourceName, "spaces.0.id"), + testAccCheckOutputExists("octopus_space_id"), + resource.TestCheckOutput("octopus_space_id", spaceID), + ), + }, + }, + }) +} + +func testAccDataSourceSpacesConfig(spaceID string) string { + tfConfig := fmt.Sprintf(` + data "octopusdeploy_spaces" "test" { + ids = ["%s"] + skip = 0 + take = 1 + } + + output "octopus_space_id" { + value = data.octopusdeploy_spaces.test.spaces[0].id + } + `, spaceID) + return tfConfig +} + +func testAccCheckOutputExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Outputs[name] + if !ok { + return fmt.Errorf("output %s not found", name) + } + return nil + } +} diff --git a/octopusdeploy_framework/resource_variable_test.go b/octopusdeploy_framework/resource_variable_test.go index 055d796d4..d71cc6dd8 100644 --- a/octopusdeploy_framework/resource_variable_test.go +++ b/octopusdeploy_framework/resource_variable_test.go @@ -3,7 +3,9 @@ package octopusdeploy_framework import ( "fmt" "path/filepath" + "strings" "testing" + "time" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" internalTest "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/test" @@ -320,6 +322,9 @@ func TestVariableResource(t *testing.T) { // Assert client, err := octoclient.CreateClient(octoContainer.URI, newSpaceId, test.ApiKey) project, err := client.Projects.GetByName("Test") + + // Add a short delay before querying the API + time.Sleep(5 * time.Second) variableSet, err := client.Variables.GetAll(project.ID) if err != nil { @@ -327,7 +332,15 @@ func TestVariableResource(t *testing.T) { } if len(variableSet.Variables) != 7 { - t.Fatalf("Expected 7 variables to be created.") + var report strings.Builder + report.WriteString(fmt.Sprintf("Expected 7 variables, but found %d.\nReturned variables:\n", len(variableSet.Variables))) + + for _, v := range variableSet.Variables { + report.WriteString(fmt.Sprintf("- Name: %s\n Type: %s\n Value: %s\n Scope: %+v\n\n", + v.Name, v.Type, v.Value, v.Scope)) + } + + t.Fatalf(report.String()) } for _, variable := range variableSet.Variables { diff --git a/octopusdeploy_framework/schemas/schema.go b/octopusdeploy_framework/schemas/schema.go index 5c786144b..395aefc7f 100644 --- a/octopusdeploy_framework/schemas/schema.go +++ b/octopusdeploy_framework/schemas/schema.go @@ -191,7 +191,12 @@ func GetBooleanResourceAttribute(description string, defaultValue bool, isOption func GetIds(ids types.List) []string { var result = make([]string, 0, len(ids.Elements())) for _, id := range ids.Elements() { - result = append(result, id.String()) + strVal, ok := id.(types.String) + + if !ok || strVal.IsNull() || strVal.IsUnknown() { + continue + } + result = append(result, strVal.ValueString()) } return result } diff --git a/terraform/49-variables/variables.tf b/terraform/49-variables/variables.tf index 9ebdc692c..0e2f15da7 100644 --- a/terraform/49-variables/variables.tf +++ b/terraform/49-variables/variables.tf @@ -1,21 +1,35 @@ + + +resource "octopusdeploy_variable" "scoped_project_variable_action" { + depends_on = [ + octopusdeploy_project.test_project, + ] + owner_id = octopusdeploy_project.test_project.id + type = "String" + name = "ActionScopedVariable" + value = "unscoped variable" + scope { + actions = [octopusdeploy_deployment_process.test_deployment_process.step[0].run_script_action[0].id] + } +} + resource "octopusdeploy_variable" "unscoped_project_variable" { + depends_on = [ + octopusdeploy_project.test_project, + octopusdeploy_variable.scoped_project_variable_action, + ] owner_id = octopusdeploy_project.test_project.id type = "String" name = "UnscopedVariable" value = "UnscopedVariable" } -resource "octopusdeploy_variable" "scoped_project_variable_action" { - owner_id = octopusdeploy_project.test_project.id - type = "String" - name = "ActionScopedVariable" - value = "unscoped variable" - scope { - actions = [octopusdeploy_deployment_process.test_deployment_process.step[0].run_script_action[0].id] - } -} - resource "octopusdeploy_variable" "scoped_project_variable_channel" { + depends_on = [ + octopusdeploy_project.test_project, + octopusdeploy_variable.unscoped_project_variable, + octopusdeploy_variable.scoped_project_variable_action, + ] owner_id = octopusdeploy_project.test_project.id type = "String" name = "ChannelScopedVariable" @@ -26,6 +40,12 @@ resource "octopusdeploy_variable" "scoped_project_variable_channel" { } resource "octopusdeploy_variable" "scoped_project_variable_environment" { + depends_on = [ + octopusdeploy_project.test_project, + octopusdeploy_variable.unscoped_project_variable, + octopusdeploy_variable.scoped_project_variable_action, + octopusdeploy_variable.scoped_project_variable_channel, + ] owner_id = octopusdeploy_project.test_project.id type = "String" name = "EnvironmentScopedVariable" @@ -36,6 +56,14 @@ resource "octopusdeploy_variable" "scoped_project_variable_environment" { } resource "octopusdeploy_variable" "scoped_project_variable_machine" { + depends_on = [ + octopusdeploy_project.test_project, + octopusdeploy_variable.unscoped_project_variable, + octopusdeploy_variable.scoped_project_variable_action, + octopusdeploy_variable.scoped_project_variable_channel, + octopusdeploy_variable.scoped_project_variable_environment + ] + owner_id = octopusdeploy_project.test_project.id type = "String" name = "MachineScopedVariable" @@ -46,6 +74,14 @@ resource "octopusdeploy_variable" "scoped_project_variable_machine" { } resource "octopusdeploy_variable" "scoped_project_variable_process" { + depends_on = [ + octopusdeploy_project.test_project, + octopusdeploy_variable.unscoped_project_variable, + octopusdeploy_variable.scoped_project_variable_action, + octopusdeploy_variable.scoped_project_variable_channel, + octopusdeploy_variable.scoped_project_variable_environment, + octopusdeploy_variable.scoped_project_variable_machine, + ] owner_id = octopusdeploy_project.test_project.id type = "String" name = "ProcessScopedVariable" @@ -56,6 +92,16 @@ resource "octopusdeploy_variable" "scoped_project_variable_process" { } resource "octopusdeploy_variable" "scoped_project_variable_role" { + depends_on = [ + octopusdeploy_project.test_project, + octopusdeploy_variable.unscoped_project_variable, + octopusdeploy_variable.scoped_project_variable_action, + octopusdeploy_variable.scoped_project_variable_channel, + octopusdeploy_variable.scoped_project_variable_environment, + octopusdeploy_variable.scoped_project_variable_machine, + octopusdeploy_variable.scoped_project_variable_process, + ] + owner_id = octopusdeploy_project.test_project.id type = "String" name = "RoleScopedVariable" @@ -63,4 +109,4 @@ resource "octopusdeploy_variable" "scoped_project_variable_role" { scope { roles = ["role"] } -} +} \ No newline at end of file From 3a8a027a764cda034b8840bbba6df254e5f0d6b4 Mon Sep 17 00:00:00 2001 From: Isaac Calligeros <101079287+IsaacCalligeros95@users.noreply.github.com> Date: Thu, 8 Aug 2024 10:23:22 +0930 Subject: [PATCH 03/12] Isaac/mc schema fixes (#715) * propagate an error * The collection returned by data sources must not be optional * Add more optionals --------- Co-authored-by: Matthew Casperson --- octopusdeploy/data_source_tenants.go | 5 ++++- octopusdeploy/schema_account_resource.go | 1 + .../schema_azure_cloud_service_deployment_target.go | 2 +- .../schema_azure_service_fabric_cluster_deployment_target.go | 2 +- octopusdeploy/schema_azure_web_app_deployment_target.go | 2 +- octopusdeploy/schema_certificate.go | 2 +- octopusdeploy/schema_channel.go | 2 +- octopusdeploy/schema_cloud_region_deployment_target.go | 2 +- octopusdeploy/schema_deployment_target.go | 2 +- octopusdeploy/schema_kubernetes_agent_deployment_target.go | 2 +- octopusdeploy/schema_kubernetes_cluster_deployment_target.go | 2 +- octopusdeploy/schema_listening_tentacle_deployment_target.go | 2 +- octopusdeploy/schema_machine_policy.go | 2 +- .../schema_offline_package_drop_deployment_target.go | 2 +- octopusdeploy/schema_polling_tentacle_deployment_target.go | 2 +- octopusdeploy/schema_script_modules.go | 2 +- octopusdeploy/schema_ssh_connection_deployment_target.go | 2 +- octopusdeploy/schema_tag_set.go | 2 +- octopusdeploy/schema_team.go | 2 +- octopusdeploy/schema_tenant.go | 2 +- octopusdeploy/schema_user.go | 2 +- octopusdeploy/schema_user_role.go | 2 +- octopusdeploy/schema_worker_pool.go | 2 +- octopusdeploy_framework/schemas/gitCredential.go | 1 + octopusdeploy_framework/schemas/lifecycle.go | 1 + octopusdeploy_framework/schemas/project.go | 1 + 26 files changed, 29 insertions(+), 22 deletions(-) diff --git a/octopusdeploy/data_source_tenants.go b/octopusdeploy/data_source_tenants.go index fcbdceae8..4c9cfa569 100644 --- a/octopusdeploy/data_source_tenants.go +++ b/octopusdeploy/data_source_tenants.go @@ -44,7 +44,10 @@ func dataSourceTenantsRead(ctx context.Context, d *schema.ResourceData, meta int flattenedTenants = append(flattenedTenants, flattenTenant(tenant)) } - d.Set("tenants", flattenedTenants) + if err := d.Set("tenants", flattenedTenants); err != nil { + return diag.FromErr(err) + } + d.SetId("Tenants " + time.Now().UTC().String()) return nil diff --git a/octopusdeploy/schema_account_resource.go b/octopusdeploy/schema_account_resource.go index 5dbee9361..4a31f5017 100644 --- a/octopusdeploy/schema_account_resource.go +++ b/octopusdeploy/schema_account_resource.go @@ -50,6 +50,7 @@ func getAccountResourceDataSchema() map[string]*schema.Schema { Description: "A list of accounts that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, Type: schema.TypeList, + Optional: false, }, "id": getDataSchemaID(), "space_id": getQuerySpaceID(), diff --git a/octopusdeploy/schema_azure_cloud_service_deployment_target.go b/octopusdeploy/schema_azure_cloud_service_deployment_target.go index 46488a934..45e00de20 100644 --- a/octopusdeploy/schema_azure_cloud_service_deployment_target.go +++ b/octopusdeploy/schema_azure_cloud_service_deployment_target.go @@ -70,7 +70,7 @@ func getAzureCloudServiceDeploymentTargetDataSchema() map[string]*schema.Schema Computed: true, Description: "A list of Azure cloud service deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_azure_service_fabric_cluster_deployment_target.go b/octopusdeploy/schema_azure_service_fabric_cluster_deployment_target.go index 3f8af8905..639d08553 100644 --- a/octopusdeploy/schema_azure_service_fabric_cluster_deployment_target.go +++ b/octopusdeploy/schema_azure_service_fabric_cluster_deployment_target.go @@ -85,7 +85,7 @@ func getAzureServiceFabricClusterDeploymentTargetDataSchema() map[string]*schema Computed: true, Description: "A list of Azure service fabric cluster deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_azure_web_app_deployment_target.go b/octopusdeploy/schema_azure_web_app_deployment_target.go index 8478378b6..f8b2cfe25 100644 --- a/octopusdeploy/schema_azure_web_app_deployment_target.go +++ b/octopusdeploy/schema_azure_web_app_deployment_target.go @@ -55,7 +55,7 @@ func getAzureWebAppDeploymentTargetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of Azure web app deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_certificate.go b/octopusdeploy/schema_certificate.go index 4252f9899..2d35164c9 100644 --- a/octopusdeploy/schema_certificate.go +++ b/octopusdeploy/schema_certificate.go @@ -170,7 +170,7 @@ func getCertificateDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of certificates that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "first_result": getQueryFirstResult(), diff --git a/octopusdeploy/schema_channel.go b/octopusdeploy/schema_channel.go index 88aa0bda9..eed2eead6 100644 --- a/octopusdeploy/schema_channel.go +++ b/octopusdeploy/schema_channel.go @@ -73,7 +73,7 @@ func getChannelDataSchema() map[string]*schema.Schema { Computed: true, Description: "A channel that matches the specified filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "ids": getQueryIDs(), diff --git a/octopusdeploy/schema_cloud_region_deployment_target.go b/octopusdeploy/schema_cloud_region_deployment_target.go index bce3dd18e..8e2375e07 100644 --- a/octopusdeploy/schema_cloud_region_deployment_target.go +++ b/octopusdeploy/schema_cloud_region_deployment_target.go @@ -40,7 +40,7 @@ func getCloudRegionDeploymentTargetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of cloud region deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_deployment_target.go b/octopusdeploy/schema_deployment_target.go index 456265859..9649a7bec 100644 --- a/octopusdeploy/schema_deployment_target.go +++ b/octopusdeploy/schema_deployment_target.go @@ -90,7 +90,7 @@ func getDeploymentTargetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "environments": getQueryEnvironments(), diff --git a/octopusdeploy/schema_kubernetes_agent_deployment_target.go b/octopusdeploy/schema_kubernetes_agent_deployment_target.go index 822e7dce8..2323f311b 100644 --- a/octopusdeploy/schema_kubernetes_agent_deployment_target.go +++ b/octopusdeploy/schema_kubernetes_agent_deployment_target.go @@ -173,7 +173,7 @@ func getKubernetesAgentDeploymentTargetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of kubernetes agent deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_kubernetes_cluster_deployment_target.go b/octopusdeploy/schema_kubernetes_cluster_deployment_target.go index 6dcbd6a35..4704688ff 100644 --- a/octopusdeploy/schema_kubernetes_cluster_deployment_target.go +++ b/octopusdeploy/schema_kubernetes_cluster_deployment_target.go @@ -131,7 +131,7 @@ func getKubernetesClusterDeploymentTargetDataSchema() map[string]*schema.Schema Computed: true, Description: "A list of Kubernetes cluster deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_listening_tentacle_deployment_target.go b/octopusdeploy/schema_listening_tentacle_deployment_target.go index 32d2b3481..a35cdb8af 100644 --- a/octopusdeploy/schema_listening_tentacle_deployment_target.go +++ b/octopusdeploy/schema_listening_tentacle_deployment_target.go @@ -57,7 +57,7 @@ func getListeningTentacleDeploymentTargetDataSchema() map[string]*schema.Schema Computed: true, Description: "A list of listening tentacle deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_machine_policy.go b/octopusdeploy/schema_machine_policy.go index 537da34df..0d24b2f6c 100644 --- a/octopusdeploy/schema_machine_policy.go +++ b/octopusdeploy/schema_machine_policy.go @@ -112,7 +112,7 @@ func getMachinePolicyDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of machine policies that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "partial_name": getQueryPartialName(), diff --git a/octopusdeploy/schema_offline_package_drop_deployment_target.go b/octopusdeploy/schema_offline_package_drop_deployment_target.go index f8b88f704..371a08b01 100644 --- a/octopusdeploy/schema_offline_package_drop_deployment_target.go +++ b/octopusdeploy/schema_offline_package_drop_deployment_target.go @@ -51,7 +51,7 @@ func getOfflinePackageDropDeploymentTargetDataSchema() map[string]*schema.Schema Computed: true, Description: "A list of offline package drop deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_polling_tentacle_deployment_target.go b/octopusdeploy/schema_polling_tentacle_deployment_target.go index ab6472663..ee73b0cfb 100644 --- a/octopusdeploy/schema_polling_tentacle_deployment_target.go +++ b/octopusdeploy/schema_polling_tentacle_deployment_target.go @@ -51,7 +51,7 @@ func getPollingTentacleDeploymentTargetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of polling tentacle deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_script_modules.go b/octopusdeploy/schema_script_modules.go index c4a24d1f8..906d2ea54 100644 --- a/octopusdeploy/schema_script_modules.go +++ b/octopusdeploy/schema_script_modules.go @@ -86,7 +86,7 @@ func getScriptModuleDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of script modules that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "partial_name": getQueryPartialName(), diff --git a/octopusdeploy/schema_ssh_connection_deployment_target.go b/octopusdeploy/schema_ssh_connection_deployment_target.go index 90934132f..df977c19d 100644 --- a/octopusdeploy/schema_ssh_connection_deployment_target.go +++ b/octopusdeploy/schema_ssh_connection_deployment_target.go @@ -57,7 +57,7 @@ func getSSHConnectionDeploymentTargetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of SSH connection deployment targets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, } diff --git a/octopusdeploy/schema_tag_set.go b/octopusdeploy/schema_tag_set.go index db5c31a23..953b5c40c 100644 --- a/octopusdeploy/schema_tag_set.go +++ b/octopusdeploy/schema_tag_set.go @@ -54,7 +54,7 @@ func getTagSetDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of tag sets that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "take": getQueryTake(), diff --git a/octopusdeploy/schema_team.go b/octopusdeploy/schema_team.go index 3f782a1c9..d0656662e 100644 --- a/octopusdeploy/schema_team.go +++ b/octopusdeploy/schema_team.go @@ -85,7 +85,7 @@ func getTeamDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of teams that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, } diff --git a/octopusdeploy/schema_tenant.go b/octopusdeploy/schema_tenant.go index 6045f5981..260ba93f8 100644 --- a/octopusdeploy/schema_tenant.go +++ b/octopusdeploy/schema_tenant.go @@ -67,7 +67,7 @@ func getTenantDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of tenants that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, "take": getQueryTake(), diff --git a/octopusdeploy/schema_user.go b/octopusdeploy/schema_user.go index 5ef089c67..620c3db37 100644 --- a/octopusdeploy/schema_user.go +++ b/octopusdeploy/schema_user.go @@ -74,7 +74,7 @@ func getUserDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of users that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, } diff --git a/octopusdeploy/schema_user_role.go b/octopusdeploy/schema_user_role.go index 9258402e5..4487a9170 100644 --- a/octopusdeploy/schema_user_role.go +++ b/octopusdeploy/schema_user_role.go @@ -84,7 +84,7 @@ func getUserRoleDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of user roles that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, } diff --git a/octopusdeploy/schema_worker_pool.go b/octopusdeploy/schema_worker_pool.go index 9207124c2..036e2a04a 100644 --- a/octopusdeploy/schema_worker_pool.go +++ b/octopusdeploy/schema_worker_pool.go @@ -39,7 +39,7 @@ func getWorkerPoolDataSchema() map[string]*schema.Schema { Computed: true, Description: "A list of worker pools that match the filter(s).", Elem: &schema.Resource{Schema: dataSchema}, - Optional: true, + Optional: false, Type: schema.TypeList, }, } diff --git a/octopusdeploy_framework/schemas/gitCredential.go b/octopusdeploy_framework/schemas/gitCredential.go index 2a29f1b4d..594f927b1 100644 --- a/octopusdeploy_framework/schemas/gitCredential.go +++ b/octopusdeploy_framework/schemas/gitCredential.go @@ -54,6 +54,7 @@ func GetGitCredentialDataSourceSchema() map[string]datasourceSchema.Attribute { "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(), diff --git a/octopusdeploy_framework/schemas/lifecycle.go b/octopusdeploy_framework/schemas/lifecycle.go index 092d48ab6..04f7f493a 100644 --- a/octopusdeploy_framework/schemas/lifecycle.go +++ b/octopusdeploy_framework/schemas/lifecycle.go @@ -97,6 +97,7 @@ func GetDatasourceLifecycleSchema() datasourceSchema.Schema { "take": util.GetQueryTakeDatasourceSchema(), "lifecycles": datasourceSchema.ListNestedAttribute{ Computed: true, + Optional: false, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ "id": util.GetIdDatasourceSchema(), diff --git a/octopusdeploy_framework/schemas/project.go b/octopusdeploy_framework/schemas/project.go index fc4fee3b7..814710bda 100644 --- a/octopusdeploy_framework/schemas/project.go +++ b/octopusdeploy_framework/schemas/project.go @@ -193,6 +193,7 @@ func getProjectsDataSourceAttribute() datasourceSchema.ListNestedAttribute { return datasourceSchema.ListNestedAttribute{ Description: "A list of projects that match the filter(s).", Computed: true, + Optional: false, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ "allow_deployments_to_no_targets": util.DataSourceBool().Computed().Deprecated("Allow deployments to be created when there are no targets.").Build(), 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 04/12] 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]() } From b1a1cd2f7f0d7638e05cd8b54a1bb76f6d8ae6cb Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 7 Aug 2024 22:38:06 -0700 Subject: [PATCH 05/12] chore: ensure resources deleted from Octopus are removed from state (#720) --- go.mod | 2 +- go.sum | 2 ++ internal/errors/error.go | 22 +++++++++++++++++++ .../resource_artifactory_generic_feed.go | 6 ++++- ...resource_aws_elastic_container_registry.go | 6 ++++- .../resource_docker_container_registry.go | 6 ++++- .../resource_environment.go | 6 ++++- .../resource_git_credential.go | 9 ++++++-- .../resource_github_repository_feed.go | 6 ++++- octopusdeploy_framework/resource_helm_feed.go | 6 ++++- .../resource_library_variable_set.go | 8 +++++-- octopusdeploy_framework/resource_lifecycle.go | 17 +++++++++----- .../resource_lifecycle_test.go | 7 +++--- .../resource_maven_feed.go | 6 ++++- .../resource_nuget_feed.go | 6 ++++- octopusdeploy_framework/resource_project.go | 6 ++++- .../resource_project_flatten.go | 4 +++- .../resource_project_group.go | 6 ++++- .../resource_project_model.go | 4 +++- octopusdeploy_framework/resource_space.go | 8 +++++-- .../resource_tenant_common_variable.go | 3 ++- .../resource_tenant_project.go | 3 ++- .../resource_tenant_project_variable.go | 6 +++-- octopusdeploy_framework/resource_variable.go | 5 ++++- .../schemas/artifactory_generic_feed.go | 3 ++- .../schemas/aws_elastic_container_registry.go | 3 ++- .../schemas/docker_container_registry_feed.go | 3 ++- .../schemas/environment.go | 3 ++- .../schemas/github_repository_feed.go | 3 ++- octopusdeploy_framework/schemas/helm_feed.go | 3 ++- .../schemas/library_variable_set.go | 3 ++- octopusdeploy_framework/schemas/maven_feed.go | 3 ++- octopusdeploy_framework/schemas/nuget_feed.go | 3 ++- .../schemas/project_group.go | 3 ++- octopusdeploy_framework/schemas/resource.go | 19 ++++++++++++++++ octopusdeploy_framework/schemas/space.go | 3 ++- octopusdeploy_framework/schemas/variable.go | 6 +++-- 37 files changed, 173 insertions(+), 45 deletions(-) create mode 100644 octopusdeploy_framework/schemas/resource.go diff --git a/go.mod b/go.mod index 2cad3a7e8..4cc35d04a 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637 github.com/hashicorp/terraform-plugin-docs v0.13.0 - github.com/hashicorp/terraform-plugin-framework v1.9.0 + github.com/hashicorp/terraform-plugin-framework v1.11.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-log v0.9.0 diff --git a/go.sum b/go.sum index fd5a915a5..c917536b0 100644 --- a/go.sum +++ b/go.sum @@ -166,6 +166,8 @@ github.com/hashicorp/terraform-plugin-docs v0.13.0 h1:6e+VIWsVGb6jYJewfzq2ok2smP github.com/hashicorp/terraform-plugin-docs v0.13.0/go.mod h1:W0oCmHAjIlTHBbvtppWHe8fLfZ2BznQbuv8+UD8OucQ= github.com/hashicorp/terraform-plugin-framework v1.9.0 h1:caLcDoxiRucNi2hk8+j3kJwkKfvHznubyFsJMWfZqKU= github.com/hashicorp/terraform-plugin-framework v1.9.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= +github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE= +github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= diff --git a/internal/errors/error.go b/internal/errors/error.go index 21c2d4f74..733b46675 100644 --- a/internal/errors/error.go +++ b/internal/errors/error.go @@ -6,6 +6,8 @@ import ( "net/http" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -29,3 +31,23 @@ func ProcessApiError(ctx context.Context, d *schema.ResourceData, err error, res return diag.FromErr(err) } + +func DeleteFromStateV2(ctx context.Context, resp *resource.ReadResponse, resource schemas.IResourceModel, resourceDescription string) error { + log.Printf("[INFO] %s (%s) not found; deleting from state", resourceDescription, resource.GetID()) + resp.State.RemoveResource(ctx) + return nil +} + +func ProcessApiErrorV2(ctx context.Context, resp *resource.ReadResponse, resource schemas.IResourceModel, err error, resourceDescription string) error { + if err == nil { + return nil + } + + if apiError, ok := err.(*core.APIError); ok { + if apiError.StatusCode == http.StatusNotFound { + return DeleteFromStateV2(ctx, resp, resource, resourceDescription) + } + } + + return nil +} diff --git a/octopusdeploy_framework/resource_artifactory_generic_feed.go b/octopusdeploy_framework/resource_artifactory_generic_feed.go index a1eb7ab58..b6c765d51 100644 --- a/octopusdeploy_framework/resource_artifactory_generic_feed.go +++ b/octopusdeploy_framework/resource_artifactory_generic_feed.go @@ -3,7 +3,9 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -77,7 +79,9 @@ func (r *artifactoryGenericFeedTypeResource) Read(ctx context.Context, req resou client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load artifactoryGeneric feed", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "artifactory generic feed"); err != nil { + resp.Diagnostics.AddError("unable to load artifactoryGeneric feed", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_aws_elastic_container_registry.go b/octopusdeploy_framework/resource_aws_elastic_container_registry.go index 72cef3b51..daeb36f31 100644 --- a/octopusdeploy_framework/resource_aws_elastic_container_registry.go +++ b/octopusdeploy_framework/resource_aws_elastic_container_registry.go @@ -3,8 +3,10 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -78,7 +80,9 @@ func (r *awsElasticContainerRegistryFeedTypeResource) Read(ctx context.Context, client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load aws elastic container registry", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "aws elastic container registry"); err != nil { + resp.Diagnostics.AddError("unable to load aws elastic container registry", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_docker_container_registry.go b/octopusdeploy_framework/resource_docker_container_registry.go index fa73ef902..0b246633e 100644 --- a/octopusdeploy_framework/resource_docker_container_registry.go +++ b/octopusdeploy_framework/resource_docker_container_registry.go @@ -3,8 +3,10 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -76,7 +78,9 @@ func (r *dockerContainerRegistryFeedTypeResource) Read(ctx context.Context, req client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load docker container registry feed", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "docker container registry feed"); err != nil { + resp.Diagnostics.AddError("unable to load docker container registry feed", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_environment.go b/octopusdeploy_framework/resource_environment.go index a393279ad..281639f6d 100644 --- a/octopusdeploy_framework/resource_environment.go +++ b/octopusdeploy_framework/resource_environment.go @@ -5,6 +5,7 @@ import ( "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/extensions" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -82,7 +83,10 @@ func (r *environmentTypeResource) Read(ctx context.Context, req resource.ReadReq environment, err := environments.GetByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load environment", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "environment"); err != nil { + resp.Diagnostics.AddError("unable to load environment", err.Error()) + } + return } updateEnvironment(ctx, &data, environment) diff --git a/octopusdeploy_framework/resource_git_credential.go b/octopusdeploy_framework/resource_git_credential.go index 478b4f8b4..8a5baeec1 100644 --- a/octopusdeploy_framework/resource_git_credential.go +++ b/octopusdeploy_framework/resource_git_credential.go @@ -2,8 +2,10 @@ package octopusdeploy_framework import ( "context" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/credentials" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -18,13 +20,14 @@ type gitCredentialResource struct { } type gitCredentialResourceModel struct { - ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` Type types.String `tfsdk:"type"` Username types.String `tfsdk:"username"` Password types.String `tfsdk:"password"` + + schemas.ResourceModel } func NewGitCredentialResource() resource.Resource { @@ -92,7 +95,9 @@ func (g *gitCredentialResource) Read(ctx context.Context, req resource.ReadReque gitCredential, err := credentials.GetByID(g.Client, state.SpaceID.ValueString(), state.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("Error reading Git credential", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, state, err, "git credential"); err != nil { + resp.Diagnostics.AddError("Error reading Git credential", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_github_repository_feed.go b/octopusdeploy_framework/resource_github_repository_feed.go index 30f7bdb28..6b6aabb63 100644 --- a/octopusdeploy_framework/resource_github_repository_feed.go +++ b/octopusdeploy_framework/resource_github_repository_feed.go @@ -3,8 +3,10 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -78,7 +80,9 @@ func (r *githubRepositoryFeedTypeResource) Read(ctx context.Context, req resourc client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load github repository feed", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "github repository feed"); err != nil { + resp.Diagnostics.AddError("unable to load github repository feed", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_helm_feed.go b/octopusdeploy_framework/resource_helm_feed.go index 64ec52677..dd7667eff 100644 --- a/octopusdeploy_framework/resource_helm_feed.go +++ b/octopusdeploy_framework/resource_helm_feed.go @@ -3,7 +3,9 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -77,7 +79,9 @@ func (r *helmFeedTypeResource) Read(ctx context.Context, req resource.ReadReques client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load helm feed", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "helm feed"); err != nil { + resp.Diagnostics.AddError("unable to load helm feed", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_library_variable_set.go b/octopusdeploy_framework/resource_library_variable_set.go index 733b88664..cdf616aa0 100644 --- a/octopusdeploy_framework/resource_library_variable_set.go +++ b/octopusdeploy_framework/resource_library_variable_set.go @@ -3,12 +3,14 @@ package octopusdeploy_framework import ( "context" "fmt" + "log" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/libraryvariablesets" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-log/tflog" - "log" ) type libraryVariableSetFeedTypeResource struct { @@ -61,7 +63,9 @@ func (r *libraryVariableSetFeedTypeResource) Read(ctx context.Context, req resou libraryVariableSet, err := libraryvariablesets.GetByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load library variable set", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "library variable set"); err != nil { + resp.Diagnostics.AddError("unable to load library variable set", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_lifecycle.go b/octopusdeploy_framework/resource_lifecycle.go index afdea2ef6..a4c863f8c 100644 --- a/octopusdeploy_framework/resource_lifecycle.go +++ b/octopusdeploy_framework/resource_lifecycle.go @@ -3,8 +3,11 @@ package octopusdeploy_framework import ( "context" "fmt" + "strings" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/lifecycles" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -12,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "strings" ) type lifecycleTypeResource struct { @@ -23,13 +25,14 @@ var _ resource.Resource = &lifecycleTypeResource{} var _ resource.ResourceWithImportState = &lifecycleTypeResource{} type lifecycleTypeResourceModel struct { - ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` Phase types.List `tfsdk:"phase"` ReleaseRetentionPolicy types.List `tfsdk:"release_retention_policy"` TentacleRetentionPolicy types.List `tfsdk:"tentacle_retention_policy"` + + schemas.ResourceModel } func NewLifecycleResource() resource.Resource { @@ -88,7 +91,9 @@ func (r *lifecycleTypeResource) Read(ctx context.Context, req resource.ReadReque lifecycle, err := lifecycles.GetByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load lifecycle", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "lifecycle"); err != nil { + resp.Diagnostics.AddError("unable to load lifecycle", err.Error()) + } return } data = flattenLifecycleResource(lifecycle) @@ -195,8 +200,7 @@ func setDefaultRetentionPolicies(data *lifecycleTypeResourceModel) { } func flattenLifecycleResource(lifecycle *lifecycles.Lifecycle) *lifecycleTypeResourceModel { - return &lifecycleTypeResourceModel{ - ID: types.StringValue(lifecycle.ID), + flattenedLifecycle := &lifecycleTypeResourceModel{ SpaceID: types.StringValue(lifecycle.SpaceID), Name: types.StringValue(lifecycle.Name), Description: types.StringValue(lifecycle.Description), @@ -204,6 +208,9 @@ func flattenLifecycleResource(lifecycle *lifecycles.Lifecycle) *lifecycleTypeRes ReleaseRetentionPolicy: flattenRetentionPeriod(lifecycle.ReleaseRetentionPolicy), TentacleRetentionPolicy: flattenRetentionPeriod(lifecycle.TentacleRetentionPolicy), } + flattenedLifecycle.ID = types.StringValue(lifecycle.GetID()) + + return flattenedLifecycle } func flattenPhases(phases []*lifecycles.Phase) types.List { diff --git a/octopusdeploy_framework/resource_lifecycle_test.go b/octopusdeploy_framework/resource_lifecycle_test.go index 639fc6a93..bbcc08030 100644 --- a/octopusdeploy_framework/resource_lifecycle_test.go +++ b/octopusdeploy_framework/resource_lifecycle_test.go @@ -2,6 +2,9 @@ package octopusdeploy_framework import ( "fmt" + "path/filepath" + "testing" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/lifecycles" @@ -13,8 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/stretchr/testify/require" - "path/filepath" - "testing" ) func TestExpandLifecycleWithNil(t *testing.T) { @@ -31,7 +32,6 @@ func TestExpandLifecycle(t *testing.T) { tentacleRetention := core.NewRetentionPeriod(2, "Items", false) data := &lifecycleTypeResourceModel{ - ID: types.StringValue(Id), Description: types.StringValue(description), Name: types.StringValue(name), SpaceID: types.StringValue(spaceID), @@ -62,6 +62,7 @@ func TestExpandLifecycle(t *testing.T) { }, ), } + data.ID = types.StringValue(Id) lifecycle := expandLifecycle(data) diff --git a/octopusdeploy_framework/resource_maven_feed.go b/octopusdeploy_framework/resource_maven_feed.go index 722081e81..55346da60 100644 --- a/octopusdeploy_framework/resource_maven_feed.go +++ b/octopusdeploy_framework/resource_maven_feed.go @@ -3,8 +3,10 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -76,7 +78,9 @@ func (r *mavenFeedTypeResource) Read(ctx context.Context, req resource.ReadReque client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load maven feed", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "maven feed"); err != nil { + resp.Diagnostics.AddError("unable to load maven feed", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_nuget_feed.go b/octopusdeploy_framework/resource_nuget_feed.go index f053ea450..bf0576248 100644 --- a/octopusdeploy_framework/resource_nuget_feed.go +++ b/octopusdeploy_framework/resource_nuget_feed.go @@ -3,8 +3,10 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -78,7 +80,9 @@ func (r *nugetFeedTypeResource) Read(ctx context.Context, req resource.ReadReque client := r.Config.Client feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load nuget feed", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "nuget feed"); err != nil { + resp.Diagnostics.AddError("unable to load nuget feed", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_project.go b/octopusdeploy_framework/resource_project.go index 6b73a6d7a..bcefbbf69 100644 --- a/octopusdeploy_framework/resource_project.go +++ b/octopusdeploy_framework/resource_project.go @@ -3,7 +3,9 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -92,7 +94,9 @@ func (r *projectResource) Read(ctx context.Context, req resource.ReadRequest, re project, err := projects.GetByID(r.Client, state.SpaceID.ValueString(), state.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("Error reading project", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, state, err, "lifecycle"); err != nil { + resp.Diagnostics.AddError("Error reading project", err.Error()) + } return } if persistenceSettings != nil { diff --git a/octopusdeploy_framework/resource_project_flatten.go b/octopusdeploy_framework/resource_project_flatten.go index 39b4c3e80..6e411b08d 100644 --- a/octopusdeploy_framework/resource_project_flatten.go +++ b/octopusdeploy_framework/resource_project_flatten.go @@ -3,6 +3,7 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/actiontemplates" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/credentials" @@ -26,7 +27,6 @@ func flattenProject(ctx context.Context, project *projects.Project, state *proje } model := &projectResourceModel{ - ID: types.StringValue(project.GetID()), SpaceID: types.StringValue(project.SpaceID), Name: types.StringValue(project.Name), Description: types.StringValue(project.Description), @@ -48,6 +48,8 @@ func flattenProject(ctx context.Context, project *projects.Project, state *proje ClonedFromProjectID: util.StringOrNull(project.ClonedFromProjectID), } + model.ID = types.StringValue(project.GetID()) + model.IncludedLibraryVariableSets = util.FlattenStringList(project.IncludedLibraryVariableSets) model.AutoDeployReleaseOverrides = flattenAutoDeployReleaseOverrides(project.AutoDeployReleaseOverrides) diff --git a/octopusdeploy_framework/resource_project_group.go b/octopusdeploy_framework/resource_project_group.go index e78f49cef..8d7a628de 100644 --- a/octopusdeploy_framework/resource_project_group.go +++ b/octopusdeploy_framework/resource_project_group.go @@ -3,7 +3,9 @@ package octopusdeploy_framework import ( "context" "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projectgroups" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -67,7 +69,9 @@ func (r *projectGroupTypeResource) Read(ctx context.Context, req resource.ReadRe group, err := projectgroups.GetByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load project group", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "project group"); err != nil { + resp.Diagnostics.AddError("unable to load project group", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_project_model.go b/octopusdeploy_framework/resource_project_model.go index 7e7386079..d58409893 100644 --- a/octopusdeploy_framework/resource_project_model.go +++ b/octopusdeploy_framework/resource_project_model.go @@ -1,11 +1,11 @@ package octopusdeploy_framework import ( + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/hashicorp/terraform-plugin-framework/types" ) type projectResourceModel struct { - ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` @@ -37,6 +37,8 @@ type projectResourceModel struct { ServiceNowExtensionSettings types.List `tfsdk:"servicenow_extension_settings"` IncludedLibraryVariableSets types.List `tfsdk:"included_library_variable_sets"` AutoDeployReleaseOverrides types.List `tfsdk:"auto_deploy_release_overrides"` + + schemas.ResourceModel } type connectivityPolicyModel struct { diff --git a/octopusdeploy_framework/resource_space.go b/octopusdeploy_framework/resource_space.go index 01cf13cdb..42470551a 100644 --- a/octopusdeploy_framework/resource_space.go +++ b/octopusdeploy_framework/resource_space.go @@ -3,7 +3,10 @@ package octopusdeploy_framework import ( "context" "fmt" + "strings" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/spaces" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/path" @@ -11,7 +14,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "strings" ) const spaceManagersTeamIDPrefix = "teams-spacemanagers-" @@ -129,7 +131,9 @@ func (s *spaceResource) Read(ctx context.Context, req resource.ReadRequest, resp spaceResult, err := spaces.GetByID(s.Client, data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to query spaces", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "space"); err != nil { + resp.Diagnostics.AddError("unable to query spaces", err.Error()) + } return } diff --git a/octopusdeploy_framework/resource_tenant_common_variable.go b/octopusdeploy_framework/resource_tenant_common_variable.go index 5ce0bd4a3..715f306bb 100644 --- a/octopusdeploy_framework/resource_tenant_common_variable.go +++ b/octopusdeploy_framework/resource_tenant_common_variable.go @@ -24,12 +24,13 @@ type tenantCommonVariableResource struct { } type tenantCommonVariableResourceModel struct { - ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` TenantID types.String `tfsdk:"tenant_id"` LibraryVariableSetID types.String `tfsdk:"library_variable_set_id"` TemplateID types.String `tfsdk:"template_id"` Value types.String `tfsdk:"value"` + + schemas.ResourceModel } func NewTenantCommonVariableResource() resource.Resource { diff --git a/octopusdeploy_framework/resource_tenant_project.go b/octopusdeploy_framework/resource_tenant_project.go index f860af3e3..37a6879c9 100644 --- a/octopusdeploy_framework/resource_tenant_project.go +++ b/octopusdeploy_framework/resource_tenant_project.go @@ -21,11 +21,12 @@ import ( ) type TenantProjectModel struct { - ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` TenantID types.String `tfsdk:"tenant_id"` ProjectID types.String `tfsdk:"project_id"` EnvironmentIDs types.List `tfsdk:"environment_ids"` + + schemas.ResourceModel } type tenantProjectResource struct { diff --git a/octopusdeploy_framework/resource_tenant_project_variable.go b/octopusdeploy_framework/resource_tenant_project_variable.go index 7c7fb8191..7718f658e 100644 --- a/octopusdeploy_framework/resource_tenant_project_variable.go +++ b/octopusdeploy_framework/resource_tenant_project_variable.go @@ -3,6 +3,8 @@ package octopusdeploy_framework import ( "context" "fmt" + "strings" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" @@ -12,7 +14,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" - "strings" ) var _ resource.Resource = &tenantProjectVariableResource{} @@ -23,13 +24,14 @@ type tenantProjectVariableResource struct { } type tenantProjectVariableResourceModel struct { - ID types.String `tfsdk:"id"` SpaceID types.String `tfsdk:"space_id"` TenantID types.String `tfsdk:"tenant_id"` ProjectID types.String `tfsdk:"project_id"` EnvironmentID types.String `tfsdk:"environment_id"` TemplateID types.String `tfsdk:"template_id"` Value types.String `tfsdk:"value"` + + schemas.ResourceModel } func NewTenantProjectVariableResource() resource.Resource { diff --git a/octopusdeploy_framework/resource_variable.go b/octopusdeploy_framework/resource_variable.go index 2b411f50c..78d48b5a2 100644 --- a/octopusdeploy_framework/resource_variable.go +++ b/octopusdeploy_framework/resource_variable.go @@ -7,6 +7,7 @@ import ( "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -125,7 +126,9 @@ func (r *variableTypeResource) Read(ctx context.Context, req resource.ReadReques variable, err := variables.GetByID(r.Config.Client, data.SpaceID.ValueString(), variableOwnerID.ValueString(), data.ID.ValueString()) if err != nil { - resp.Diagnostics.AddError("unable to load variable", err.Error()) + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, schemas.VariableResourceDescription); err != nil { + resp.Diagnostics.AddError("unable to load variable", err.Error()) + } return } diff --git a/octopusdeploy_framework/schemas/artifactory_generic_feed.go b/octopusdeploy_framework/schemas/artifactory_generic_feed.go index 9ee7623f8..aecde79de 100644 --- a/octopusdeploy_framework/schemas/artifactory_generic_feed.go +++ b/octopusdeploy_framework/schemas/artifactory_generic_feed.go @@ -33,7 +33,6 @@ func GetArtifactoryGenericFeedResourceSchema() map[string]resourceSchema.Attribu type ArtifactoryGenericFeedTypeResourceModel struct { FeedUri types.String `tfsdk:"feed_uri"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Password types.String `tfsdk:"password"` @@ -41,4 +40,6 @@ type ArtifactoryGenericFeedTypeResourceModel struct { Username types.String `tfsdk:"username"` Repository types.String `tfsdk:"repository"` LayoutRegex types.String `tfsdk:"layout_regex"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/aws_elastic_container_registry.go b/octopusdeploy_framework/schemas/aws_elastic_container_registry.go index 92c3c847b..fbc321f9e 100644 --- a/octopusdeploy_framework/schemas/aws_elastic_container_registry.go +++ b/octopusdeploy_framework/schemas/aws_elastic_container_registry.go @@ -32,10 +32,11 @@ func GetAwsElasticContainerRegistryFeedResourceSchema() map[string]resourceSchem type AwsElasticContainerRegistryFeedTypeResourceModel struct { AccessKey types.String `tfsdk:"access_key"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Region types.String `tfsdk:"region"` SecretKey types.String `tfsdk:"secret_key"` SpaceID types.String `tfsdk:"space_id"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/docker_container_registry_feed.go b/octopusdeploy_framework/schemas/docker_container_registry_feed.go index d7ff550c4..346eee869 100644 --- a/octopusdeploy_framework/schemas/docker_container_registry_feed.go +++ b/octopusdeploy_framework/schemas/docker_container_registry_feed.go @@ -29,11 +29,12 @@ func GetDockerContainerRegistryFeedResourceSchema() map[string]resourceSchema.At type DockerContainerRegistryFeedTypeResourceModel struct { APIVersion types.String `tfsdk:"api_version"` FeedUri types.String `tfsdk:"feed_uri"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Password types.String `tfsdk:"password"` SpaceID types.String `tfsdk:"space_id"` Username types.String `tfsdk:"username"` RegistryPath types.String `tfsdk:"registry_path"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/environment.go b/octopusdeploy_framework/schemas/environment.go index af4a31e20..964532446 100644 --- a/octopusdeploy_framework/schemas/environment.go +++ b/octopusdeploy_framework/schemas/environment.go @@ -180,7 +180,6 @@ func MapServiceNowExtensionSettings(serviceNowExtensionSettings *environments.Se } type EnvironmentTypeResourceModel struct { - ID types.String `tfsdk:"id"` Slug types.String `tfsdk:"slug"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` @@ -191,4 +190,6 @@ type EnvironmentTypeResourceModel struct { JiraExtensionSettings types.List `tfsdk:"jira_extension_settings"` JiraServiceManagementExtensionSettings types.List `tfsdk:"jira_service_management_extension_settings"` ServiceNowExtensionSettings types.List `tfsdk:"servicenow_extension_settings"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/github_repository_feed.go b/octopusdeploy_framework/schemas/github_repository_feed.go index 12cda7b7e..71049f464 100644 --- a/octopusdeploy_framework/schemas/github_repository_feed.go +++ b/octopusdeploy_framework/schemas/github_repository_feed.go @@ -26,10 +26,11 @@ type GitHubRepositoryFeedTypeResourceModel struct { DownloadAttempts types.Int64 `tfsdk:"download_attempts"` DownloadRetryBackoffSeconds types.Int64 `tfsdk:"download_retry_backoff_seconds"` FeedUri types.String `tfsdk:"feed_uri"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Password types.String `tfsdk:"password"` SpaceID types.String `tfsdk:"space_id"` Username types.String `tfsdk:"username"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/helm_feed.go b/octopusdeploy_framework/schemas/helm_feed.go index 62b567b60..7e3072fee 100644 --- a/octopusdeploy_framework/schemas/helm_feed.go +++ b/octopusdeploy_framework/schemas/helm_feed.go @@ -22,10 +22,11 @@ func GetHelmFeedResourceSchema() map[string]resourceSchema.Attribute { type HelmFeedTypeResourceModel struct { FeedUri types.String `tfsdk:"feed_uri"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Password types.String `tfsdk:"password"` SpaceID types.String `tfsdk:"space_id"` Username types.String `tfsdk:"username"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/library_variable_set.go b/octopusdeploy_framework/schemas/library_variable_set.go index 5fab75d02..3ca65b627 100644 --- a/octopusdeploy_framework/schemas/library_variable_set.go +++ b/octopusdeploy_framework/schemas/library_variable_set.go @@ -12,12 +12,13 @@ import ( type LibraryVariableSetResourceModel struct { Description types.String `tfsdk:"description"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` SpaceID types.String `tfsdk:"space_id"` Template types.List `tfsdk:"template"` TemplateIds types.Map `tfsdk:"template_ids"` VariableSetId types.String `tfsdk:"variable_set_id"` + + ResourceModel } func GetLibraryVariableSetDataSourceSchema() datasourceSchema.Schema { diff --git a/octopusdeploy_framework/schemas/maven_feed.go b/octopusdeploy_framework/schemas/maven_feed.go index 096d7b4f4..9c01debc3 100644 --- a/octopusdeploy_framework/schemas/maven_feed.go +++ b/octopusdeploy_framework/schemas/maven_feed.go @@ -26,10 +26,11 @@ type MavenFeedTypeResourceModel struct { DownloadAttempts types.Int64 `tfsdk:"download_attempts"` DownloadRetryBackoffSeconds types.Int64 `tfsdk:"download_retry_backoff_seconds"` FeedUri types.String `tfsdk:"feed_uri"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Password types.String `tfsdk:"password"` SpaceID types.String `tfsdk:"space_id"` Username types.String `tfsdk:"username"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/nuget_feed.go b/octopusdeploy_framework/schemas/nuget_feed.go index a9d5567fb..a744dafb1 100644 --- a/octopusdeploy_framework/schemas/nuget_feed.go +++ b/octopusdeploy_framework/schemas/nuget_feed.go @@ -33,11 +33,12 @@ type NugetFeedTypeResourceModel struct { DownloadAttempts types.Int64 `tfsdk:"download_attempts"` DownloadRetryBackoffSeconds types.Int64 `tfsdk:"download_retry_backoff_seconds"` FeedUri types.String `tfsdk:"feed_uri"` - ID types.String `tfsdk:"id"` IsEnhancedMode types.Bool `tfsdk:"is_enhanced_mode"` Name types.String `tfsdk:"name"` PackageAcquisitionLocationOptions types.List `tfsdk:"package_acquisition_location_options"` Password types.String `tfsdk:"password"` SpaceID types.String `tfsdk:"space_id"` Username types.String `tfsdk:"username"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/project_group.go b/octopusdeploy_framework/schemas/project_group.go index 1aaf65d18..bafc5ea42 100644 --- a/octopusdeploy_framework/schemas/project_group.go +++ b/octopusdeploy_framework/schemas/project_group.go @@ -43,9 +43,10 @@ func GetProjectGroupResourceSchema() map[string]resourceSchema.Attribute { } type ProjectGroupTypeResourceModel struct { - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` SpaceID types.String `tfsdk:"space_id"` Description types.String `tfsdk:"description"` RetentionPolicyID types.String `tfsdk:"retention_policy_id"` + + ResourceModel } diff --git a/octopusdeploy_framework/schemas/resource.go b/octopusdeploy_framework/schemas/resource.go new file mode 100644 index 000000000..b97eb5efc --- /dev/null +++ b/octopusdeploy_framework/schemas/resource.go @@ -0,0 +1,19 @@ +package schemas + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type IResourceModel interface { + GetID() string +} + +type ResourceModel struct { + ID types.String `tfsdk:"id"` + + IResourceModel `tfsdk:"-"` // Ignore resource model interface in object conversion +} + +func (r ResourceModel) GetID() string { + return r.ID.ValueString() +} diff --git a/octopusdeploy_framework/schemas/space.go b/octopusdeploy_framework/schemas/space.go index c14354752..662a95c96 100644 --- a/octopusdeploy_framework/schemas/space.go +++ b/octopusdeploy_framework/schemas/space.go @@ -11,7 +11,6 @@ import ( const spaceDescription = "space" type SpaceModel struct { - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Slug types.String `tfsdk:"slug"` Description types.String `tfsdk:"description"` @@ -19,6 +18,8 @@ type SpaceModel struct { SpaceManagersTeams types.Set `tfsdk:"space_managers_teams"` SpaceManagersTeamMembers types.Set `tfsdk:"space_managers_team_members"` IsTaskQueueStopped types.Bool `tfsdk:"is_task_queue_stopped"` + + ResourceModel } func GetSpaceResourceSchema() map[string]resourceSchema.Attribute { diff --git a/octopusdeploy_framework/schemas/variable.go b/octopusdeploy_framework/schemas/variable.go index b3195028b..5d9836e99 100644 --- a/octopusdeploy_framework/schemas/variable.go +++ b/octopusdeploy_framework/schemas/variable.go @@ -218,7 +218,6 @@ func GetVariableResourceSchema() resourceSchema.Schema { } type VariableTypeResourceModel struct { - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` Description types.String `tfsdk:"description"` OwnerID types.String `tfsdk:"owner_id"` @@ -234,6 +233,8 @@ type VariableTypeResourceModel struct { Prompt types.List `tfsdk:"prompt"` Scope types.List `tfsdk:"scope"` SpaceID types.String `tfsdk:"space_id"` + + ResourceModel } type VariablesDataSourceModel struct { @@ -242,11 +243,12 @@ type VariablesDataSourceModel struct { Scope types.List `tfsdk:"scope"` SpaceID types.String `tfsdk:"space_id"` Description types.String `tfsdk:"description"` - ID types.String `tfsdk:"id"` IsEditable types.Bool `tfsdk:"is_editable"` IsSensitive types.Bool `tfsdk:"is_sensitive"` Prompt types.List `tfsdk:"prompt"` SensitiveValue types.String `tfsdk:"sensitive_value"` Type types.String `tfsdk:"type"` Value types.String `tfsdk:"value"` + + ResourceModel } From 7ac25f7e821378b13a78088411332565f665a8d8 Mon Sep 17 00:00:00 2001 From: Isaac Calligeros <101079287+IsaacCalligeros95@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:29:25 +0930 Subject: [PATCH 06/12] Chore!: Migrate tenant datasource (#699) * Migrate tenants datasource * Update previously migrated data source schemas to be read only --- .../azure_cloud_service_deployment_targets.md | 28 ++--- ...rvice_fabric_cluster_deployment_targets.md | 30 ++--- .../azure_web_app_deployment_targets.md | 28 ++--- docs/data-sources/certificates.md | 26 ++--- docs/data-sources/channels.md | 26 ++--- .../cloud_region_deployment_targets.md | 28 ++--- docs/data-sources/deployment_targets.md | 28 ++--- docs/data-sources/environments.md | 20 ++-- docs/data-sources/feeds.md | 23 ++-- docs/data-sources/git_credentials.md | 24 ++-- .../kubernetes_agent_deployment_targets.md | 44 +++---- .../kubernetes_cluster_deployment_targets.md | 56 ++++----- docs/data-sources/library_variable_sets.md | 14 +-- .../listening_tentacle_deployment_targets.md | 52 ++++----- docs/data-sources/machine_policies.md | 38 +++---- ...offline_package_drop_deployment_targets.md | 32 +++--- .../polling_tentacle_deployment_targets.md | 32 +++--- docs/data-sources/project_groups.md | 19 ++-- docs/data-sources/script_modules | 18 +-- docs/data-sources/script_modules.md | 18 +-- docs/data-sources/space.md | 2 +- docs/data-sources/spaces.md | 17 +-- .../ssh_connection_deployment_targets.md | 28 ++--- docs/data-sources/tag_sets.md | 14 +-- docs/data-sources/teams.md | 20 ++-- docs/data-sources/tenants.md | 10 +- docs/data-sources/user_roles.md | 10 +- docs/data-sources/users.md | 18 +-- docs/data-sources/worker_pools.md | 14 +-- docs/index.md | 5 +- docs/resources/azure_subscription_account.md | 4 +- docs/resources/environment.md | 29 +++-- docs/resources/lifecycle.md | 4 +- docs/resources/nuget_feed.md | 4 +- docs/resources/space.md | 4 +- docs/resources/tenant_common_variable.md | 19 ++-- docs/resources/tenant_project.md | 13 +-- go.mod | 2 +- go.sum | 4 +- octopusdeploy/data_source_tenants.go | 54 --------- octopusdeploy/provider.go | 2 - .../resource_deployment_process_test.go | 1 - octopusdeploy/testing_container_test.go | 2 +- .../datasource_environments.go | 4 +- .../datasource_project_groups.go | 4 +- octopusdeploy_framework/datasource_spaces.go | 2 +- octopusdeploy_framework/datasource_tenants.go | 80 +++++++++++++ .../datasource_tenants_test.go | 11 +- octopusdeploy_framework/framework_provider.go | 3 +- .../schemas/environment.go | 15 +-- octopusdeploy_framework/schemas/feed.go | 33 +++--- .../schemas/library_variable_set.go | 14 +-- .../schemas/project_group.go | 2 +- octopusdeploy_framework/schemas/schema.go | 58 ++++++++-- octopusdeploy_framework/schemas/space.go | 23 ++-- octopusdeploy_framework/schemas/tenant.go | 107 ++++++++++++++++++ .../testing_container_test.go | 2 +- octopusdeploy_framework/util/schema.go | 40 ++++--- 58 files changed, 708 insertions(+), 554 deletions(-) delete mode 100644 octopusdeploy/data_source_tenants.go create mode 100644 octopusdeploy_framework/datasource_tenants.go rename octopusdeploy/data_source_tenants_test.go => octopusdeploy_framework/datasource_tenants_test.go (82%) create mode 100644 octopusdeploy_framework/schemas/tenant.go diff --git a/docs/data-sources/azure_cloud_service_deployment_targets.md b/docs/data-sources/azure_cloud_service_deployment_targets.md index 5bd56afb8..c202843d6 100644 --- a/docs/data-sources/azure_cloud_service_deployment_targets.md +++ b/docs/data-sources/azure_cloud_service_deployment_targets.md @@ -45,10 +45,10 @@ data "octopusdeploy_azure_cloud_service_deployment_targets" "example" { ### Read-Only -- `azure_cloud_service_deployment_targets` (Block List) A list of Azure cloud service deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--azure_cloud_service_deployment_targets)) +- `azure_cloud_service_deployment_targets` (List of Object) A list of Azure cloud service deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--azure_cloud_service_deployment_targets)) - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. - + ### Nested Schema for `azure_cloud_service_deployment_targets` Read-Only: @@ -56,33 +56,33 @@ Read-Only: - `account_id` (String) - `cloud_service_name` (String) - `default_worker_pool_id` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--azure_cloud_service_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--azure_cloud_service_deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) - `slot` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) - `storage_account_name` (String) - `swap_if_possible` (Boolean) -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - `use_current_instance_count` (Boolean) - + ### Nested Schema for `azure_cloud_service_deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md b/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md index f6aedf261..13a82ca92 100644 --- a/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md +++ b/docs/data-sources/azure_service_fabric_cluster_deployment_targets.md @@ -45,47 +45,47 @@ data "octopusdeploy_azure_service_fabric_cluster_deployment_targets" "example" { ### Read-Only -- `azure_service_fabric_cluster_deployment_targets` (Block List) A list of Azure service fabric cluster deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--azure_service_fabric_cluster_deployment_targets)) +- `azure_service_fabric_cluster_deployment_targets` (List of Object) A list of Azure service fabric cluster deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--azure_service_fabric_cluster_deployment_targets)) - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. - + ### Nested Schema for `azure_service_fabric_cluster_deployment_targets` Read-Only: - `aad_client_credential_secret` (String) - `aad_credential_type` (String) -- `aad_user_credential_password` (String, Sensitive) +- `aad_user_credential_password` (String) - `aad_user_credential_username` (String) - `certificate_store_location` (String) - `certificate_store_name` (String) - `client_certificate_variable` (String) - `connection_endpoint` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--azure_service_fabric_cluster_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--azure_service_fabric_cluster_deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `roles` (List of String) - `security_mode` (String) - `server_certificate_thumbprint` (String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - + ### Nested Schema for `azure_service_fabric_cluster_deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/azure_web_app_deployment_targets.md b/docs/data-sources/azure_web_app_deployment_targets.md index 26a8b8a06..a5f00dd3d 100644 --- a/docs/data-sources/azure_web_app_deployment_targets.md +++ b/docs/data-sources/azure_web_app_deployment_targets.md @@ -45,41 +45,41 @@ data "octopusdeploy_azure_web_app_deployment_targets" "example" { ### Read-Only -- `azure_web_app_deployment_targets` (Block List) A list of Azure web app deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--azure_web_app_deployment_targets)) +- `azure_web_app_deployment_targets` (List of Object) A list of Azure web app deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--azure_web_app_deployment_targets)) - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. - + ### Nested Schema for `azure_web_app_deployment_targets` Read-Only: - `account_id` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--azure_web_app_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--azure_web_app_deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `resource_group_name` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - `web_app_name` (String) - `web_app_slot_name` (String) - + ### Nested Schema for `azure_web_app_deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/certificates.md b/docs/data-sources/certificates.md index 9ba533b85..a62462d0b 100644 --- a/docs/data-sources/certificates.md +++ b/docs/data-sources/certificates.md @@ -40,29 +40,29 @@ data "octopusdeploy_certificates" "example" { ### Read-Only -- `certificates` (Block List) A list of certificates that match the filter(s). (see [below for nested schema](#nestedblock--certificates)) +- `certificates` (List of Object) A list of certificates that match the filter(s). (see [below for nested schema](#nestedatt--certificates)) - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. - + ### Nested Schema for `certificates` Read-Only: - `archived` (String) -- `certificate_data` (String, Sensitive) The encoded data of the certificate. -- `certificate_data_format` (String) Specifies the archive file format used for storing cryptography objects in the certificate. Valid formats are `Der`, `Pem`, `Pkcs12`, or `Unknown`. -- `environments` (List of String) A list of environment IDs associated with this resource. -- `has_private_key` (Boolean) Indicates if the certificate has a private key. -- `id` (String) The unique ID for this resource. -- `is_expired` (Boolean) Indicates if the certificate has expired. +- `certificate_data` (String) +- `certificate_data_format` (String) +- `environments` (List of String) +- `has_private_key` (Boolean) +- `id` (String) +- `is_expired` (Boolean) - `issuer_common_name` (String) - `issuer_distinguished_name` (String) - `issuer_organization` (String) -- `name` (String) The name of this resource. +- `name` (String) - `not_after` (String) - `not_before` (String) - `notes` (String) -- `password` (String, Sensitive) The password associated with this resource. +- `password` (String) - `replaced_by` (String) - `self_signed` (Boolean) - `serial_number` (String) @@ -72,9 +72,9 @@ Read-Only: - `subject_common_name` (String) - `subject_distinguished_name` (String) - `subject_organization` (String) -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `version` (Number) diff --git a/docs/data-sources/channels.md b/docs/data-sources/channels.md index bf1546f4f..b19b11d00 100644 --- a/docs/data-sources/channels.md +++ b/docs/data-sources/channels.md @@ -34,25 +34,25 @@ data "octopusdeploy_channels" "example" { ### Read-Only -- `channels` (Block List) A channel that matches the specified filter(s). (see [below for nested schema](#nestedblock--channels)) +- `channels` (List of Object) A channel that matches the specified filter(s). (see [below for nested schema](#nestedatt--channels)) - `id` (String) The ID of this resource. - + ### Nested Schema for `channels` Read-Only: -- `description` (String) The description of this channel. -- `id` (String) The unique ID for this resource. -- `is_default` (Boolean) Indicates if this is the default channel for the associated project. -- `lifecycle_id` (String) The lifecycle ID associated with this channel. -- `name` (String) The name of this resource. -- `project_id` (String) The project ID associated with this channel. -- `rule` (List of Object) A list of rules associated with this channel. (see [below for nested schema](#nestedatt--channels--rule)) -- `space_id` (String) The space ID associated with this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. - - +- `description` (String) +- `id` (String) +- `is_default` (Boolean) +- `lifecycle_id` (String) +- `name` (String) +- `project_id` (String) +- `rule` (List of Object) (see [below for nested schema](#nestedobjatt--channels--rule)) +- `space_id` (String) +- `tenant_tags` (List of String) + + ### Nested Schema for `channels.rule` Read-Only: diff --git a/docs/data-sources/cloud_region_deployment_targets.md b/docs/data-sources/cloud_region_deployment_targets.md index 79adef44b..8818118d5 100644 --- a/docs/data-sources/cloud_region_deployment_targets.md +++ b/docs/data-sources/cloud_region_deployment_targets.md @@ -46,38 +46,38 @@ data "octopusdeploy_cloud_region_deployment_targets" "example" { ### Read-Only -- `cloud_region_deployment_targets` (Block List) A list of cloud region deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--cloud_region_deployment_targets)) +- `cloud_region_deployment_targets` (List of Object) A list of cloud region deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--cloud_region_deployment_targets)) - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. - + ### Nested Schema for `cloud_region_deployment_targets` Read-Only: - `default_worker_pool_id` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--cloud_region_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--cloud_region_deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - + ### Nested Schema for `cloud_region_deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/deployment_targets.md b/docs/data-sources/deployment_targets.md index 4677decd0..50a12ed38 100644 --- a/docs/data-sources/deployment_targets.md +++ b/docs/data-sources/deployment_targets.md @@ -36,37 +36,37 @@ Provides information about existing deployment targets. ### Read-Only -- `deployment_targets` (Block List) A list of deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--deployment_targets)) +- `deployment_targets` (List of Object) A list of deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--deployment_targets)) - `id` (String) The ID of this resource. - + ### Nested Schema for `deployment_targets` Read-Only: -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - + ### Nested Schema for `deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/environments.md b/docs/data-sources/environments.md index 69bc1ab23..d67c25219 100644 --- a/docs/data-sources/environments.md +++ b/docs/data-sources/environments.md @@ -28,16 +28,16 @@ data "octopusdeploy_environments" "example" { ### Optional - `ids` (List of String) A filter to search by a list of IDs. -- `name` (String) A filter to search by name. -- `partial_name` (String) A filter to search by the partial match of a name. +- `name` (String) A filter search by exact name +- `partial_name` (String) A filter to search by a partial name. - `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 environment. - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `environments` (Block List) A list of environments that match the filter(s). (see [below for nested schema](#nestedblock--environments)) -- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. +- `environments` (Block List) Provides information about existing environments. (see [below for nested schema](#nestedblock--environments)) +- `id` (String) The unique ID for this resource. ### Nested Schema for `environments` @@ -47,12 +47,12 @@ Read-Only: - `allow_dynamic_infrastructure` (Boolean) - `description` (String) The description of this environment. - `id` (String) The unique ID for this resource. -- `jira_extension_settings` (List of Object) Provides extension settings for the Jira integration for this environment. (see [below for nested schema](#nestedatt--environments--jira_extension_settings)) -- `jira_service_management_extension_settings` (List of Object) Provides extension settings for the Jira Service Management (JSM) integration for this environment. (see [below for nested schema](#nestedatt--environments--jira_service_management_extension_settings)) +- `jira_extension_settings` (Attributes List) Provides extension settings for the Jira integration for this environment. (see [below for nested schema](#nestedatt--environments--jira_extension_settings)) +- `jira_service_management_extension_settings` (Attributes List) Provides extension settings for the Jira Service Management (JSM) integration for this environment. (see [below for nested schema](#nestedatt--environments--jira_service_management_extension_settings)) - `name` (String) The name of this resource. -- `servicenow_extension_settings` (List of Object) Provides extension settings for the ServiceNow integration for this environment. (see [below for nested schema](#nestedatt--environments--servicenow_extension_settings)) -- `slug` (String) -- `sort_order` (Number) The order number to sort an environment. +- `servicenow_extension_settings` (Attributes List) Provides extension settings for the ServiceNow integration for this environment. (see [below for nested schema](#nestedatt--environments--servicenow_extension_settings)) +- `slug` (String) The unique slug of this environment +- `sort_order` (Number) The order number to sort an environment - `space_id` (String) The space ID associated with this environment. - `use_guided_failure` (Boolean) diff --git a/docs/data-sources/feeds.md b/docs/data-sources/feeds.md index 91fde3c96..7afdb726d 100644 --- a/docs/data-sources/feeds.md +++ b/docs/data-sources/feeds.md @@ -27,8 +27,6 @@ data "octopusdeploy_feeds" "example" { ### Optional - `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`. -- `feeds` (Block List) (see [below for nested schema](#nestedblock--feeds)) -- `id` (String) The unique ID for this resource. - `ids` (List of String) A filter to search by a list of IDs. - `name` (String) The name of this resource. - `partial_name` (String) A filter to search by a partial name. @@ -36,31 +34,30 @@ data "octopusdeploy_feeds" "example" { - `space_id` (String) The space ID associated with this feeds. - `take` (Number) A filter to specify the number of items to take (or return) in the response. +### Read-Only + +- `feeds` (Block List) (see [below for nested schema](#nestedblock--feeds)) +- `id` (String) The unique ID for this resource. + ### Nested Schema for `feeds` -Required: +Read-Only: - `access_key` (String) The AWS access key to use when authenticating against Amazon Web Services. -- `feed_uri` (String) -- `name` (String) The name of this resource. - -Optional: - - `api_version` (String) - `delete_unreleased_packages_after_days` (Number) - `download_attempts` (Number) The number of times a deployment should attempt to download a package from this feed before failing. - `download_retry_backoff_seconds` (Number) The number of seconds to apply as a linear back off between download attempts. - `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`. +- `feed_uri` (String) - `id` (String) The unique ID for this resource. - `is_enhanced_mode` (Boolean) +- `name` (String) The name of this resource. - `package_acquisition_location_options` (List of String) - `password` (String, Sensitive) The password associated with this resource. +- `region` (String) - `registry_path` (String) - `secret_key` (String, Sensitive) - `space_id` (String) The space ID associated with this feeds. -- `username` (String, Sensitive) The username associated with this resource. - -Read-Only: - -- `region` (String) \ No newline at end of file +- `username` (String, Sensitive) The username associated with this resource. \ No newline at end of file diff --git a/docs/data-sources/git_credentials.md b/docs/data-sources/git_credentials.md index d5e0820a4..e7a4a3c4d 100644 --- a/docs/data-sources/git_credentials.md +++ b/docs/data-sources/git_credentials.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_git_credentials Data Source - terraform-provider-octopusdeploy" subcategory: "" description: |- - Provides information about existing GitCredentials. + Use this data source to retrieve information about Git credentials in Octopus Deploy. --- # octopusdeploy_git_credentials (Data Source) -Provides information about existing GitCredentials. +Use this data source to retrieve information about Git credentials in Octopus Deploy. @@ -17,26 +17,26 @@ Provides information about existing GitCredentials. ### Optional -- `name` (String) A filter to search by name. -- `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. -- `take` (Number) A filter to specify the number of items to take (or return) in the response. +- `name` (String) The name of the Git Credential to filter by. +- `skip` (Number) The number of records to skip. +- `space_id` (String) The space ID associated with this Git Credential. +- `take` (Number) The number of records to take. ### Read-Only -- `git_credentials` (Block List) A list of Git Credentials that match the filter(s). (see [below for nested schema](#nestedblock--git_credentials)) -- `id` (String) The ID of this resource. +- `git_credentials` (Attributes List) Provides information about existing GitCredentials. (see [below for nested schema](#nestedatt--git_credentials)) +- `id` (String) The unique ID for this resource. - + ### Nested Schema for `git_credentials` Read-Only: -- `description` (String) The description of this Git credential. +- `description` (String) The description of this Git Credential. - `id` (String) The unique ID for this resource. -- `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. -- `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. - `username` (String) The username for the Git credential. diff --git a/docs/data-sources/kubernetes_agent_deployment_targets.md b/docs/data-sources/kubernetes_agent_deployment_targets.md index 527adad4a..dd4c61884 100644 --- a/docs/data-sources/kubernetes_agent_deployment_targets.md +++ b/docs/data-sources/kubernetes_agent_deployment_targets.md @@ -55,32 +55,32 @@ data "octopusdeploy_kubernetes_agent_deployment_targets" "kubernetes_agent_deplo ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `kubernetes_agent_deployment_targets` (Block List) A list of kubernetes agent deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--kubernetes_agent_deployment_targets)) +- `kubernetes_agent_deployment_targets` (List of Object) A list of kubernetes agent deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--kubernetes_agent_deployment_targets)) - + ### Nested Schema for `kubernetes_agent_deployment_targets` Read-Only: -- `agent_helm_release_name` (String) Name of the Helm release that the agent belongs to. -- `agent_kubernetes_namespace` (String) Name of the Kubernetes namespace where the agent is installed. -- `agent_tentacle_version` (String) Current Tentacle version of the agent -- `agent_upgrade_status` (String) Current upgrade availability status of the agent. One of 'NoUpgrades', 'UpgradeAvailable', 'UpgradeSuggested', 'UpgradeRequired' -- `agent_version` (String) Current Helm chart version of the agent. -- `communication_mode` (String) The communication mode used by the Kubernetes agent to communicate with Octopus Server. Currently, the only supported value is 'Polling'. -- `default_namespace` (String) Optional default namespace that will be used when using Kubernetes deployment steps, can be overrides within step configurations. -- `environments` (List of String) A list of environment IDs this Kubernetes agent can deploy to. -- `id` (String) The unique ID for this resource. -- `is_disabled` (Boolean) Whether the Kubernetes agent is disabled. If the agent is disabled, it will not be included in any deployments. -- `machine_policy_id` (String) Optional ID of the machine policy that the Kubernetes agent will use. If not provided the default machine policy will be used. -- `name` (String) The name of this resource. -- `roles` (List of String) A list of target roles that are associated to this Kubernetes agent. -- `space_id` (String) The space ID associated with this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. -- `thumbprint` (String) The thumbprint of the Kubernetes agent's certificate used by server to verify the identity of the agent. This is the same thumbprint that was used when installing the agent. -- `upgrade_locked` (Boolean) If enabled the Kubernetes agent will not automatically upgrade and will stay on the currently installed version, even if the associated machine policy is configured to automatically upgrade. -- `uri` (String) The URI of the Kubernetes agent's used by the server to queue messages. This is the same subscription uri that was used when installing the agent. +- `agent_helm_release_name` (String) +- `agent_kubernetes_namespace` (String) +- `agent_tentacle_version` (String) +- `agent_upgrade_status` (String) +- `agent_version` (String) +- `communication_mode` (String) +- `default_namespace` (String) +- `environments` (List of String) +- `id` (String) +- `is_disabled` (Boolean) +- `machine_policy_id` (String) +- `name` (String) +- `roles` (List of String) +- `space_id` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) +- `thumbprint` (String) +- `upgrade_locked` (Boolean) +- `uri` (String) diff --git a/docs/data-sources/kubernetes_cluster_deployment_targets.md b/docs/data-sources/kubernetes_cluster_deployment_targets.md index 4bf178018..7b7da7741 100644 --- a/docs/data-sources/kubernetes_cluster_deployment_targets.md +++ b/docs/data-sources/kubernetes_cluster_deployment_targets.md @@ -36,52 +36,52 @@ Provides information about existing Kubernetes cluster deployment targets. ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `kubernetes_cluster_deployment_targets` (Block List) A list of Kubernetes cluster deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--kubernetes_cluster_deployment_targets)) +- `kubernetes_cluster_deployment_targets` (List of Object) A list of Kubernetes cluster deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets)) - + ### Nested Schema for `kubernetes_cluster_deployment_targets` Read-Only: -- `authentication` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--authentication)) -- `aws_account_authentication` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--aws_account_authentication)) -- `azure_service_principal_authentication` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--azure_service_principal_authentication)) -- `certificate_authentication` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--certificate_authentication)) +- `authentication` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--authentication)) +- `aws_account_authentication` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--aws_account_authentication)) +- `azure_service_principal_authentication` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--azure_service_principal_authentication)) +- `certificate_authentication` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--certificate_authentication)) - `cluster_certificate` (String) - `cluster_certificate_path` (String) - `cluster_url` (String) -- `container` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--container)) +- `container` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--container)) - `container_options` (String) - `default_worker_pool_id` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. -- `gcp_account_authentication` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--gcp_account_authentication)) +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--endpoint)) +- `environments` (List of String) +- `gcp_account_authentication` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--gcp_account_authentication)) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `namespace` (String) - `operating_system` (String) -- `pod_authentication` (List of Object) (see [below for nested schema](#nestedatt--kubernetes_cluster_deployment_targets--pod_authentication)) +- `pod_authentication` (List of Object) (see [below for nested schema](#nestedobjatt--kubernetes_cluster_deployment_targets--pod_authentication)) - `proxy_id` (String) - `roles` (List of String) - `running_in_container` (Boolean) - `shell_name` (String) - `shell_version` (String) - `skip_tls_verification` (Boolean) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.authentication` Read-Only: @@ -89,7 +89,7 @@ Read-Only: - `account_id` (String) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.aws_account_authentication` Read-Only: @@ -104,7 +104,7 @@ Read-Only: - `use_instance_role` (Boolean) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.azure_service_principal_authentication` Read-Only: @@ -114,7 +114,7 @@ Read-Only: - `cluster_resource_group` (String) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.certificate_authentication` Read-Only: @@ -122,7 +122,7 @@ Read-Only: - `client_certificate` (String) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.container` Read-Only: @@ -131,7 +131,7 @@ Read-Only: - `image` (String) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.endpoint` Read-Only: @@ -235,7 +235,7 @@ Read-Only: - + ### Nested Schema for `kubernetes_cluster_deployment_targets.gcp_account_authentication` Read-Only: @@ -250,7 +250,7 @@ Read-Only: - `zone` (String) - + ### Nested Schema for `kubernetes_cluster_deployment_targets.pod_authentication` Read-Only: diff --git a/docs/data-sources/library_variable_sets.md b/docs/data-sources/library_variable_sets.md index 8b6eeb19f..9b10353eb 100644 --- a/docs/data-sources/library_variable_sets.md +++ b/docs/data-sources/library_variable_sets.md @@ -18,34 +18,34 @@ Provides information about existing library variable sets. ### Optional - `content_type` (String) A filter to search by content type. -- `id` (String) The unique ID for this resource. - `ids` (List of String) A filter to search by a list of IDs. -- `library_variable_sets` (Block List) A list of library variable sets that match the filter(s). (see [below for nested schema](#nestedblock--library_variable_sets)) - `partial_name` (String) A filter to search by a partial name. - `skip` (Number) A filter to specify the number of items to skip in the response. - `space_id` (String) The space ID associated with this library variable set. - `take` (Number) A filter to specify the number of items to take (or return) in the response. +### Read-Only + +- `id` (String) The unique ID for this resource. +- `library_variable_sets` (Block List) A list of library variable sets that match the filter(s). (see [below for nested schema](#nestedblock--library_variable_sets)) + ### Nested Schema for `library_variable_sets` -Optional: +Read-Only: - `description` (String) The description of this library variable set. - `id` (String) The unique ID for this resource. - `name` (String) The name of this resource. - `space_id` (String) The space ID associated with this library variable set. - `template` (List of Object) (see [below for nested schema](#nestedatt--library_variable_sets--template)) - -Read-Only: - - `template_ids` (Map of String) - `variable_set_id` (String) ### Nested Schema for `library_variable_sets.template` -Optional: +Read-Only: - `default_value` (String) - `display_settings` (Map of String) diff --git a/docs/data-sources/listening_tentacle_deployment_targets.md b/docs/data-sources/listening_tentacle_deployment_targets.md index e8f7ccb8f..c90c679f0 100644 --- a/docs/data-sources/listening_tentacle_deployment_targets.md +++ b/docs/data-sources/listening_tentacle_deployment_targets.md @@ -55,39 +55,39 @@ data "octopusdeploy_listening_tentacle_deployment_targets" "listening_tentacle_d ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `listening_tentacle_deployment_targets` (Block List) A list of listening tentacle deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--listening_tentacle_deployment_targets)) +- `listening_tentacle_deployment_targets` (List of Object) A list of listening tentacle deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--listening_tentacle_deployment_targets)) - + ### Nested Schema for `listening_tentacle_deployment_targets` Read-Only: - `certificate_signature_algorithm` (String) -- `environments` (List of String) A list of environment IDs associated with this listening tentacle. +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. -- `is_disabled` (Boolean) Represents the disabled status of this deployment target. -- `is_in_process` (Boolean) Represents the in-process status of this deployment target. -- `machine_policy_id` (String) The machine policy ID that is associated with this deployment target. -- `name` (String) The name of this resource. -- `operating_system` (String) The operating system that is associated with this deployment target. -- `proxy_id` (String) The proxy ID that is associated with this deployment target. -- `roles` (List of String) A list of role IDs that are associated with this deployment target. -- `shell_name` (String) The shell name associated with this deployment target. -- `shell_version` (String) The shell version associated with this deployment target. -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. -- `tentacle_url` (String) The tenant URL of this deployment target. -- `tentacle_version_details` (List of Object) (see [below for nested schema](#nestedatt--listening_tentacle_deployment_targets--tentacle_version_details)) -- `thumbprint` (String) The thumbprint of this deployment target. -- `uri` (String) The URI of this deployment target. - - +- `health_status` (String) +- `id` (String) +- `is_disabled` (Boolean) +- `is_in_process` (Boolean) +- `machine_policy_id` (String) +- `name` (String) +- `operating_system` (String) +- `proxy_id` (String) +- `roles` (List of String) +- `shell_name` (String) +- `shell_version` (String) +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) +- `tentacle_url` (String) +- `tentacle_version_details` (List of Object) (see [below for nested schema](#nestedobjatt--listening_tentacle_deployment_targets--tentacle_version_details)) +- `thumbprint` (String) +- `uri` (String) + + ### Nested Schema for `listening_tentacle_deployment_targets.tentacle_version_details` Read-Only: diff --git a/docs/data-sources/machine_policies.md b/docs/data-sources/machine_policies.md index 68e70b324..614a9a02e 100644 --- a/docs/data-sources/machine_policies.md +++ b/docs/data-sources/machine_policies.md @@ -26,29 +26,29 @@ Provides information about existing machine policies. ### Read-Only - `id` (String) The ID of this resource. -- `machine_policies` (Block List) A list of machine policies that match the filter(s). (see [below for nested schema](#nestedblock--machine_policies)) +- `machine_policies` (List of Object) A list of machine policies that match the filter(s). (see [below for nested schema](#nestedatt--machine_policies)) - + ### Nested Schema for `machine_policies` Read-Only: -- `connection_connect_timeout` (Number) In nanoseconds. Minimum value: 10000000000 (10 seconds). +- `connection_connect_timeout` (Number) - `connection_retry_count_limit` (Number) -- `connection_retry_sleep_interval` (Number) In nanoseconds. -- `connection_retry_time_limit` (Number) In nanoseconds. -- `description` (String) The description of this machine policy. -- `id` (String) The unique ID for this resource. +- `connection_retry_sleep_interval` (Number) +- `connection_retry_time_limit` (Number) +- `description` (String) +- `id` (String) - `is_default` (Boolean) -- `machine_cleanup_policy` (Set of Object) (see [below for nested schema](#nestedatt--machine_policies--machine_cleanup_policy)) -- `machine_connectivity_policy` (Set of Object) (see [below for nested schema](#nestedatt--machine_policies--machine_connectivity_policy)) -- `machine_health_check_policy` (Set of Object) (see [below for nested schema](#nestedatt--machine_policies--machine_health_check_policy)) -- `machine_update_policy` (Set of Object) (see [below for nested schema](#nestedatt--machine_policies--machine_update_policy)) -- `name` (String) The name of this resource. -- `polling_request_queue_timeout` (Number) In nanoseconds. -- `space_id` (String) The space ID associated with this resource. - - +- `machine_cleanup_policy` (Set of Object) (see [below for nested schema](#nestedobjatt--machine_policies--machine_cleanup_policy)) +- `machine_connectivity_policy` (Set of Object) (see [below for nested schema](#nestedobjatt--machine_policies--machine_connectivity_policy)) +- `machine_health_check_policy` (Set of Object) (see [below for nested schema](#nestedobjatt--machine_policies--machine_health_check_policy)) +- `machine_update_policy` (Set of Object) (see [below for nested schema](#nestedobjatt--machine_policies--machine_update_policy)) +- `name` (String) +- `polling_request_queue_timeout` (Number) +- `space_id` (String) + + ### Nested Schema for `machine_policies.machine_cleanup_policy` Read-Only: @@ -57,7 +57,7 @@ Read-Only: - `delete_machines_elapsed_timespan` (Number) - + ### Nested Schema for `machine_policies.machine_connectivity_policy` Read-Only: @@ -65,7 +65,7 @@ Read-Only: - `machine_connectivity_behavior` (String) - + ### Nested Schema for `machine_policies.machine_health_check_policy` Read-Only: @@ -96,7 +96,7 @@ Read-Only: - + ### Nested Schema for `machine_policies.machine_update_policy` Read-Only: diff --git a/docs/data-sources/offline_package_drop_deployment_targets.md b/docs/data-sources/offline_package_drop_deployment_targets.md index fc01cb4ad..193982d1e 100644 --- a/docs/data-sources/offline_package_drop_deployment_targets.md +++ b/docs/data-sources/offline_package_drop_deployment_targets.md @@ -36,39 +36,39 @@ Provides information about existing offline package drop deployment targets. ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `offline_package_drop_deployment_targets` (Block List) A list of offline package drop deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--offline_package_drop_deployment_targets)) +- `offline_package_drop_deployment_targets` (List of Object) A list of offline package drop deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--offline_package_drop_deployment_targets)) - + ### Nested Schema for `offline_package_drop_deployment_targets` Read-Only: - `applications_directory` (String) -- `destination` (List of Object) (see [below for nested schema](#nestedatt--offline_package_drop_deployment_targets--destination)) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--offline_package_drop_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `destination` (List of Object) (see [below for nested schema](#nestedobjatt--offline_package_drop_deployment_targets--destination)) +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--offline_package_drop_deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - `working_directory` (String) - + ### Nested Schema for `offline_package_drop_deployment_targets.destination` Read-Only: @@ -77,7 +77,7 @@ Read-Only: - `drop_folder_path` (String) - + ### Nested Schema for `offline_package_drop_deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/polling_tentacle_deployment_targets.md b/docs/data-sources/polling_tentacle_deployment_targets.md index 5124fddfc..b28cdfddf 100644 --- a/docs/data-sources/polling_tentacle_deployment_targets.md +++ b/docs/data-sources/polling_tentacle_deployment_targets.md @@ -36,39 +36,39 @@ Provides information about existing polling tentacle deployment targets. ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `polling_tentacle_deployment_targets` (Block List) A list of polling tentacle deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--polling_tentacle_deployment_targets)) +- `polling_tentacle_deployment_targets` (List of Object) A list of polling tentacle deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--polling_tentacle_deployment_targets)) - + ### Nested Schema for `polling_tentacle_deployment_targets` Read-Only: - `certificate_signature_algorithm` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--polling_tentacle_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--polling_tentacle_deployment_targets--endpoint)) +- `environments` (List of String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. -- `id` (String) The unique ID for this resource. +- `health_status` (String) +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `tentacle_url` (String) -- `tentacle_version_details` (List of Object) (see [below for nested schema](#nestedatt--polling_tentacle_deployment_targets--tentacle_version_details)) +- `tentacle_version_details` (List of Object) (see [below for nested schema](#nestedobjatt--polling_tentacle_deployment_targets--tentacle_version_details)) - `thumbprint` (String) - `uri` (String) - + ### Nested Schema for `polling_tentacle_deployment_targets.endpoint` Read-Only: @@ -172,7 +172,7 @@ Read-Only: - + ### Nested Schema for `polling_tentacle_deployment_targets.tentacle_version_details` Read-Only: diff --git a/docs/data-sources/project_groups.md b/docs/data-sources/project_groups.md index ed88f743a..ab1d0f4eb 100644 --- a/docs/data-sources/project_groups.md +++ b/docs/data-sources/project_groups.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_project_groups Data Source - terraform-provider-octopusdeploy" subcategory: "" description: |- - Provides information about existing project groups. + --- # octopusdeploy_project_groups (Data Source) -Provides information about existing project groups. + ## Example Usage @@ -27,25 +27,28 @@ data "octopusdeploy_project_groups" "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. +- `partial_name` (String) A filter to search by a partial name. +- `project_groups` (Block List) A list of project groups that match the filter(s). (see [below for nested schema](#nestedblock--project_groups)) - `skip` (Number) A filter to specify the number of items to skip in the response. -- `space_id` (String) A Space ID to filter by. Will revert what is specified on the provider if not set. +- `space_id` (String) The space ID associated with this project group. - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `project_groups` (Block List) A list of project groups that match the filter(s). (see [below for nested schema](#nestedblock--project_groups)) +- `id` (String) The unique ID for this resource. ### Nested Schema for `project_groups` -Read-Only: +Optional: - `description` (String) The description of this project group. - `id` (String) The unique ID for this resource. -- `name` (String) The name of this resource. - `retention_policy_id` (String) The ID of the retention policy associated with this project group. - `space_id` (String) The space ID associated with this project group. +Read-Only: + +- `name` (String) The name of this resource. + diff --git a/docs/data-sources/script_modules b/docs/data-sources/script_modules index 6625186eb..6a12509e7 100644 --- a/docs/data-sources/script_modules +++ b/docs/data-sources/script_modules @@ -34,21 +34,21 @@ data "octopusdeploy_script_modules" "example" { ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `script_modules` (Block List) A list of script modules that match the filter(s). (see [below for nested schema](#nestedblock--script_modules)) +- `script_modules` (List of Object) A list of script modules that match the filter(s). (see [below for nested schema](#nestedatt--script_modules)) - + ### Nested Schema for `script_modules` Read-Only: -- `description` (String) The description of this script module. -- `id` (String) The unique ID for this resource. -- `name` (String) The name of this resource. -- `script` (Set of Object) The script associated with this script module. (see [below for nested schema](#nestedatt--script_modules--script)) -- `space_id` (String) The space ID associated with this resource. -- `variable_set_id` (String) The variable set ID for this script module. +- `description` (String) +- `id` (String) +- `name` (String) +- `script` (Set of Object) (see [below for nested schema](#nestedobjatt--script_modules--script)) +- `space_id` (String) +- `variable_set_id` (String) - + ### Nested Schema for `script_modules.script` Read-Only: diff --git a/docs/data-sources/script_modules.md b/docs/data-sources/script_modules.md index 283f804cc..35d7e16a8 100644 --- a/docs/data-sources/script_modules.md +++ b/docs/data-sources/script_modules.md @@ -35,21 +35,21 @@ data "octopusdeploy_script_modules" "example" { ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `script_modules` (Block List) A list of script modules that match the filter(s). (see [below for nested schema](#nestedblock--script_modules)) +- `script_modules` (List of Object) A list of script modules that match the filter(s). (see [below for nested schema](#nestedatt--script_modules)) - + ### Nested Schema for `script_modules` Read-Only: -- `description` (String) The description of this script module. -- `id` (String) The unique ID for this resource. -- `name` (String) The name of this resource. -- `script` (Set of Object) The script associated with this script module. (see [below for nested schema](#nestedatt--script_modules--script)) -- `space_id` (String) The space ID associated with this resource. -- `variable_set_id` (String) The variable set ID for this script module. +- `description` (String) +- `id` (String) +- `name` (String) +- `script` (Set of Object) (see [below for nested schema](#nestedobjatt--script_modules--script)) +- `space_id` (String) +- `variable_set_id` (String) - + ### Nested Schema for `script_modules.script` Read-Only: diff --git a/docs/data-sources/space.md b/docs/data-sources/space.md index 36bcc4c81..2ec59a7f5 100644 --- a/docs/data-sources/space.md +++ b/docs/data-sources/space.md @@ -25,7 +25,7 @@ Provides information about an existing space. - `id` (String) The unique ID for this resource. - `is_default` (Boolean) Specifies if this space is the default space in Octopus. - `is_task_queue_stopped` (Boolean) Specifies the status of the task queue for this space. -- `slug` (String) The unique slug of this space. +- `slug` (String) The unique slug of this space - `space_managers_team_members` (Set of String) A list of user IDs designated to be managers of this space. - `space_managers_teams` (Set of String) A list of team IDs designated to be managers of this space. diff --git a/docs/data-sources/spaces.md b/docs/data-sources/spaces.md index 36485b0a1..ff5ae2193 100644 --- a/docs/data-sources/spaces.md +++ b/docs/data-sources/spaces.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_spaces Data Source - terraform-provider-octopusdeploy" subcategory: "" description: |- - Provides information about existing spaces. + --- # octopusdeploy_spaces (Data Source) -Provides information about existing spaces. + ## Example Usage @@ -27,26 +27,29 @@ data "octopusdeploy_spaces" "spaces" { ### 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. +- `partial_name` (String) A filter to search by a partial name. - `skip` (Number) A filter to specify the number of items to skip in the response. +- `spaces` (Block List) Provides information about existing spaces. (see [below for nested schema](#nestedblock--spaces)) - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `spaces` (Block List) A list of spaces that match the filter(s). (see [below for nested schema](#nestedblock--spaces)) +- `id` (String) The unique ID for this resource. ### Nested Schema for `spaces` +Required: + +- `name` (String) The name of this resource, no more than 20 characters long + Read-Only: - `description` (String) The description of this space. - `id` (String) The unique ID for this resource. - `is_default` (Boolean) Specifies if this space is the default space in Octopus. - `is_task_queue_stopped` (Boolean) Specifies the status of the task queue for this space. -- `name` (String) The name of this resource, no more than 20 characters long -- `slug` (String) The unique slug of this space. +- `slug` (String) The unique slug of this space - `space_managers_team_members` (Set of String) A list of user IDs designated to be managers of this space. - `space_managers_teams` (Set of String) A list of team IDs designated to be managers of this space. diff --git a/docs/data-sources/ssh_connection_deployment_targets.md b/docs/data-sources/ssh_connection_deployment_targets.md index 43e054adb..4c0d32f1c 100644 --- a/docs/data-sources/ssh_connection_deployment_targets.md +++ b/docs/data-sources/ssh_connection_deployment_targets.md @@ -36,42 +36,42 @@ Provides information about existing SSH connection deployment targets. ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `ssh_connection_deployment_targets` (Block List) A list of SSH connection deployment targets that match the filter(s). (see [below for nested schema](#nestedblock--ssh_connection_deployment_targets)) +- `ssh_connection_deployment_targets` (List of Object) A list of SSH connection deployment targets that match the filter(s). (see [below for nested schema](#nestedatt--ssh_connection_deployment_targets)) - + ### Nested Schema for `ssh_connection_deployment_targets` Read-Only: - `account_id` (String) - `dot_net_core_platform` (String) -- `endpoint` (List of Object) (see [below for nested schema](#nestedatt--ssh_connection_deployment_targets--endpoint)) -- `environments` (List of String) A list of environment IDs associated with this resource. +- `endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--ssh_connection_deployment_targets--endpoint)) +- `environments` (List of String) - `fingerprint` (String) - `has_latest_calamari` (Boolean) -- `health_status` (String) Represents the health status of this deployment target. Valid health statuses are `HasWarnings`, `Healthy`, `Unavailable`, `Unhealthy`, or `Unknown`. +- `health_status` (String) - `host` (String) -- `id` (String) The unique ID for this resource. +- `id` (String) - `is_disabled` (Boolean) - `is_in_process` (Boolean) - `machine_policy_id` (String) -- `name` (String) The name of this resource. +- `name` (String) - `operating_system` (String) - `port` (Number) - `proxy_id` (String) - `roles` (List of String) - `shell_name` (String) - `shell_version` (String) -- `space_id` (String) The space ID associated with this resource. -- `status` (String) The status of this resource. Valid statuses are `CalamariNeedsUpgrade`, `Disabled`, `NeedsUpgrade`, `Offline`, `Online`, or `Unknown`. -- `status_summary` (String) A summary elaborating on the status of this resource. -- `tenant_tags` (List of String) A list of tenant tags associated with this resource. -- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `tenants` (List of String) A list of tenant IDs associated with this resource. +- `space_id` (String) +- `status` (String) +- `status_summary` (String) +- `tenant_tags` (List of String) +- `tenanted_deployment_participation` (String) +- `tenants` (List of String) - `thumbprint` (String) - `uri` (String) - + ### Nested Schema for `ssh_connection_deployment_targets.endpoint` Read-Only: diff --git a/docs/data-sources/tag_sets.md b/docs/data-sources/tag_sets.md index fb8fffe78..56b41b61b 100644 --- a/docs/data-sources/tag_sets.md +++ b/docs/data-sources/tag_sets.md @@ -26,17 +26,17 @@ Provides information about existing tag sets. ### Read-Only - `id` (String) The ID of this resource. -- `tag_sets` (Block List) A list of tag sets that match the filter(s). (see [below for nested schema](#nestedblock--tag_sets)) +- `tag_sets` (List of Object) A list of tag sets that match the filter(s). (see [below for nested schema](#nestedatt--tag_sets)) - + ### Nested Schema for `tag_sets` Read-Only: -- `description` (String) The description of this tag set. -- `id` (String) The unique ID for this resource. -- `name` (String) The name of this resource. -- `sort_order` (Number) The sort order associated with this resource. -- `space_id` (String) The space ID associated with this resource. +- `description` (String) +- `id` (String) +- `name` (String) +- `sort_order` (Number) +- `space_id` (String) diff --git a/docs/data-sources/teams.md b/docs/data-sources/teams.md index ba635c2e0..1075d7295 100644 --- a/docs/data-sources/teams.md +++ b/docs/data-sources/teams.md @@ -27,9 +27,9 @@ Provides information about existing users. ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `teams` (Block List) A list of teams that match the filter(s). (see [below for nested schema](#nestedblock--teams)) +- `teams` (List of Object) A list of teams that match the filter(s). (see [below for nested schema](#nestedatt--teams)) - + ### Nested Schema for `teams` Read-Only: @@ -38,14 +38,14 @@ Read-Only: - `can_be_renamed` (Boolean) - `can_change_members` (Boolean) - `can_change_roles` (Boolean) -- `description` (String) The user-friendly description of this team. -- `external_security_group` (List of Object) (see [below for nested schema](#nestedatt--teams--external_security_group)) -- `id` (String) The unique ID for this resource. -- `name` (String) The name of this team. -- `space_id` (String) The space associated with this team. -- `users` (Set of String) A list of user IDs designated to be members of this team. - - +- `description` (String) +- `external_security_group` (List of Object) (see [below for nested schema](#nestedobjatt--teams--external_security_group)) +- `id` (String) +- `name` (String) +- `space_id` (String) +- `users` (Set of String) + + ### Nested Schema for `teams.external_security_group` Read-Only: diff --git a/docs/data-sources/tenants.md b/docs/data-sources/tenants.md index 28d093d17..7b6486318 100644 --- a/docs/data-sources/tenants.md +++ b/docs/data-sources/tenants.md @@ -21,16 +21,16 @@ Provides information about existing tenants. - `ids` (List of String) A filter to search by a list of IDs. - `is_clone` (Boolean) A filter to search for cloned resources. - `name` (String) A filter to search by name. -- `partial_name` (String) A filter to search by the partial match of a name. +- `partial_name` (String) A filter to search by a partial name. - `project_id` (String) A filter to search by a project ID. - `skip` (Number) A filter to specify the number of items to skip in the response. -- `space_id` (String) A Space ID to filter by. Will revert what is specified on the provider if not set. +- `space_id` (String) The space ID associated with this tenants. - `tags` (List of String) A filter to search by a list of tags. - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. +- `id` (String) The unique ID for this resource. - `tenants` (Block List) A list of tenants that match the filter(s). (see [below for nested schema](#nestedblock--tenants)) @@ -39,10 +39,10 @@ Provides information about existing tenants. Read-Only: - `cloned_from_tenant_id` (String) The ID of the tenant from which this tenant was cloned. -- `description` (String) The description of this tenant. +- `description` (String) The description of this tenants. - `id` (String) The unique ID for this resource. - `name` (String) The name of this resource. -- `space_id` (String) The space ID associated with this resource. +- `space_id` (String) The space ID associated with this tenant. - `tenant_tags` (List of String) A list of tenant tags associated with this resource. diff --git a/docs/data-sources/user_roles.md b/docs/data-sources/user_roles.md index 19010c518..8c6bc4544 100644 --- a/docs/data-sources/user_roles.md +++ b/docs/data-sources/user_roles.md @@ -35,19 +35,19 @@ data "octopusdeploy_user_roles" "example" { ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `user_roles` (Block List) A list of user roles that match the filter(s). (see [below for nested schema](#nestedblock--user_roles)) +- `user_roles` (List of Object) A list of user roles that match the filter(s). (see [below for nested schema](#nestedatt--user_roles)) - + ### Nested Schema for `user_roles` Read-Only: - `can_be_deleted` (Boolean) -- `description` (String) The description of this user role. +- `description` (String) - `granted_space_permissions` (List of String) - `granted_system_permissions` (List of String) -- `id` (String) The unique ID for this resource. -- `name` (String) The name of this resource. +- `id` (String) +- `name` (String) - `space_permission_descriptions` (List of String) - `supported_restrictions` (List of String) - `system_permission_descriptions` (List of String) diff --git a/docs/data-sources/users.md b/docs/data-sources/users.md index dd1a19994..ca9f963dd 100644 --- a/docs/data-sources/users.md +++ b/docs/data-sources/users.md @@ -34,25 +34,25 @@ data "octopusdeploy_users" "example" { ### Read-Only - `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `users` (Block List) A list of users that match the filter(s). (see [below for nested schema](#nestedblock--users)) +- `users` (List of Object) A list of users that match the filter(s). (see [below for nested schema](#nestedatt--users)) - + ### Nested Schema for `users` Read-Only: - `can_password_be_edited` (Boolean) -- `display_name` (String) The display name of this resource. -- `email_address` (String) The email address of this resource. -- `id` (String) The unique ID for this resource. -- `identity` (Set of Object) (see [below for nested schema](#nestedatt--users--identity)) +- `display_name` (String) +- `email_address` (String) +- `id` (String) +- `identity` (Set of Object) (see [below for nested schema](#nestedobjatt--users--identity)) - `is_active` (Boolean) - `is_requestor` (Boolean) - `is_service` (Boolean) -- `password` (String, Sensitive) The password associated with this resource. -- `username` (String, Sensitive) The username associated with this resource. +- `password` (String) +- `username` (String) - + ### Nested Schema for `users.identity` Read-Only: diff --git a/docs/data-sources/worker_pools.md b/docs/data-sources/worker_pools.md index 7c6636313..95c0cdf35 100644 --- a/docs/data-sources/worker_pools.md +++ b/docs/data-sources/worker_pools.md @@ -27,20 +27,20 @@ Provides information about existing worker pools. ### Read-Only - `id` (String) The ID of this resource. -- `worker_pools` (Block List) A list of worker pools that match the filter(s). (see [below for nested schema](#nestedblock--worker_pools)) +- `worker_pools` (List of Object) A list of worker pools that match the filter(s). (see [below for nested schema](#nestedatt--worker_pools)) - + ### Nested Schema for `worker_pools` Read-Only: - `can_add_workers` (Boolean) -- `description` (String) The description of this worker pool. -- `id` (String) The unique ID for this resource. +- `description` (String) +- `id` (String) - `is_default` (Boolean) -- `name` (String) The name of this resource. -- `sort_order` (Number) The order number to sort a dynamic worker pool. -- `space_id` (String) The space ID associated with this resource. +- `name` (String) +- `sort_order` (Number) +- `space_id` (String) - `worker_pool_type` (String) - `worker_type` (String) diff --git a/docs/index.md b/docs/index.md index 9787f53dc..e0fadfb5f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -79,11 +79,8 @@ resource "octopusdeploy_environment" "Env3" { ## Schema -### Required +### Optional - `address` (String) The endpoint of the Octopus REST API - `api_key` (String) The API key to use with the Octopus REST API - -### Optional - - `space_id` (String) The space ID to target \ No newline at end of file diff --git a/docs/resources/azure_subscription_account.md b/docs/resources/azure_subscription_account.md index a7025e91a..7f11a8822 100644 --- a/docs/resources/azure_subscription_account.md +++ b/docs/resources/azure_subscription_account.md @@ -22,9 +22,7 @@ resource "octopusdeploy_azure_subscription_account" "example" { ### Required -- `management_endpoint` (String) - `name` (String) The name of this resource. -- `storage_endpoint_suffix` (String) The storage endpoint suffix associated with this Azure subscription account. - `subscription_id` (String) The subscription ID of this resource. ### Optional @@ -34,7 +32,9 @@ resource "octopusdeploy_azure_subscription_account" "example" { - `certificate_thumbprint` (String, Sensitive) - `description` (String) The description of this Azure subscription account. - `environments` (List of String) A list of environment IDs associated with this resource. +- `management_endpoint` (String) - `space_id` (String) The space ID associated with this resource. +- `storage_endpoint_suffix` (String) The storage endpoint suffix associated with this Azure subscription account. - `tenant_tags` (List of String) A list of tenant tags associated with this resource. - `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. - `tenants` (List of String) A list of tenant IDs associated with this resource. diff --git a/docs/resources/environment.md b/docs/resources/environment.md index e8a5c7438..c9f9c5d1e 100644 --- a/docs/resources/environment.md +++ b/docs/resources/environment.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_environment Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource manages environments in Octopus Deploy. + --- # octopusdeploy_environment (Resource) -This resource manages environments in Octopus Deploy. + ## Example Usage @@ -45,39 +45,36 @@ resource "octopusdeploy_environment" "example" { - `allow_dynamic_infrastructure` (Boolean) - `description` (String) The description of this environment. - `id` (String) The unique ID for this resource. -- `jira_extension_settings` (Block List, Max: 1) Provides extension settings for the Jira integration for this environment. (see [below for nested schema](#nestedblock--jira_extension_settings)) -- `jira_service_management_extension_settings` (Block List, Max: 1) Provides extension settings for the Jira Service Management (JSM) integration for this environment. (see [below for nested schema](#nestedblock--jira_service_management_extension_settings)) -- `servicenow_extension_settings` (Block List, Max: 1) Provides extension settings for the ServiceNow integration for this environment. (see [below for nested schema](#nestedblock--servicenow_extension_settings)) -- `sort_order` (Number) The order number to sort an environment. +- `jira_extension_settings` (Block List) Provides extension settings for the Jira integration for this environment. (see [below for nested schema](#nestedblock--jira_extension_settings)) +- `jira_service_management_extension_settings` (Block List) Provides extension settings for the Jira Service Management (JSM) integration for this environment. (see [below for nested schema](#nestedblock--jira_service_management_extension_settings)) +- `servicenow_extension_settings` (Block List) Provides extension settings for the ServiceNow integration for this environment. (see [below for nested schema](#nestedblock--servicenow_extension_settings)) +- `slug` (String) The unique slug of this environment +- `sort_order` (Number) The order number to sort an environment - `space_id` (String) The space ID associated with this environment. - `use_guided_failure` (Boolean) -### Read-Only - -- `slug` (String) - ### Nested Schema for `jira_extension_settings` -Required: +Optional: -- `environment_type` (String) The Jira environment type of this Octopus deployment environment. Valid values are `"development"`, `"production"`, `"staging"`, `"testing"`, or `"unmapped"`. +- `environment_type` (String) ### Nested Schema for `jira_service_management_extension_settings` -Required: +Optional: -- `is_enabled` (Boolean) Specifies whether or not this extension is enabled for this project. +- `is_enabled` (Boolean) ### Nested Schema for `servicenow_extension_settings` -Required: +Optional: -- `is_enabled` (Boolean) Specifies whether or not this extension is enabled for this project. +- `is_enabled` (Boolean) ## Import diff --git a/docs/resources/lifecycle.md b/docs/resources/lifecycle.md index 8714ab06e..e5178b91e 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 // true only if quantity_to_keep = 0 + quantity_to_keep = 1 + should_keep_forever = true unit = "Days" } diff --git a/docs/resources/nuget_feed.md b/docs/resources/nuget_feed.md index b5758af66..4a736c70a 100644 --- a/docs/resources/nuget_feed.md +++ b/docs/resources/nuget_feed.md @@ -2,12 +2,12 @@ page_title: "octopusdeploy_nuget_feed Resource - terraform-provider-octopusdeploy" subcategory: "Feeds" description: |- - This resource manages a NuGet feed in Octopus Deploy. + This resource manages a Nuget feed in Octopus Deploy. --- # octopusdeploy_nuget_feed (Resource) -This resource manages a NuGet feed in Octopus Deploy. +This resource manages a Nuget feed in Octopus Deploy. ## Example Usage diff --git a/docs/resources/space.md b/docs/resources/space.md index 6ab45052e..f2b0f3084 100644 --- a/docs/resources/space.md +++ b/docs/resources/space.md @@ -28,7 +28,7 @@ resource "octopusdeploy_space" "example" { ### Required -- `name` (String) The name of this resource, no more than 20 characters long +- `name` (String) The name of this resource. ### Optional @@ -36,7 +36,7 @@ resource "octopusdeploy_space" "example" { - `id` (String) The unique ID for this resource. - `is_default` (Boolean) Specifies if this space is the default space in Octopus. - `is_task_queue_stopped` (Boolean) Specifies the status of the task queue for this space. -- `slug` (String) The unique slug of this space. +- `slug` (String) The unique slug of this space - `space_managers_team_members` (Set of String) A list of user IDs designated to be managers of this space. - `space_managers_teams` (Set of String) A list of team IDs designated to be managers of this space. diff --git a/docs/resources/tenant_common_variable.md b/docs/resources/tenant_common_variable.md index 4c1c54054..c306e3c59 100644 --- a/docs/resources/tenant_common_variable.md +++ b/docs/resources/tenant_common_variable.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_tenant_common_variable Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource manages tenant common variables in Octopus Deploy. + Manages a tenant common variable in Octopus Deploy. --- # octopusdeploy_tenant_common_variable (Resource) -This resource manages tenant common variables in Octopus Deploy. +Manages a tenant common variable in Octopus Deploy. @@ -17,17 +17,14 @@ This resource manages tenant common variables in Octopus Deploy. ### Required -- `library_variable_set_id` (String) -- `template_id` (String) -- `tenant_id` (String) +- `library_variable_set_id` (String) The ID of the library variable set. +- `template_id` (String) The ID of the variable template. +- `tenant_id` (String) The ID of the tenant. ### Optional -- `space_id` (String) -- `value` (String, Sensitive) - -### Read-Only - -- `id` (String) The ID of this resource. +- `id` (String) The unique ID for this resource. +- `space_id` (String) The space ID associated with this Tenant Common Variable. +- `value` (String, Sensitive) The value of the variable. diff --git a/docs/resources/tenant_project.md b/docs/resources/tenant_project.md index f537e8fcc..2558f847b 100644 --- a/docs/resources/tenant_project.md +++ b/docs/resources/tenant_project.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_tenant_project Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource represents the connection between tenants and projects. + --- # octopusdeploy_tenant_project (Resource) -This resource represents the connection between tenants and projects. + @@ -22,11 +22,8 @@ This resource represents the connection between tenants and projects. ### Optional -- `environment_ids` (List of String) The environment ID associated with this tenant. -- `space_id` (String) The space ID associated with this resource. - -### Read-Only - -- `id` (String) The ID of this resource. +- `environment_ids` (List of String) The environment IDs associated with this tenant. +- `id` (String) The unique ID for this resource. +- `space_id` (String) The space ID associated with this project tenant. diff --git a/go.mod b/go.mod index 4cc35d04a..f3ed22597 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/OctopusDeploy/go-octopusdeploy/v2 v2.49.1 - github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240725054341-2848f54d101e + github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4 github.com/google/uuid v1.6.0 github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637 github.com/hashicorp/terraform-plugin-docs v0.13.0 diff --git a/go.sum b/go.sum index c917536b0..c0bdd145e 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4 github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.49.1 h1:lzC3tcnfvC07Ilqn5J51qhOnW79kGD6nTIxCBOdmAe8= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.49.1/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw= -github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240725054341-2848f54d101e h1:FIvWa8wNg8IBG5uVhqkKvcBhaxx4TgN7T8/5Ed4VQUE= -github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240725054341-2848f54d101e/go.mod h1:Oq9KbiRNDBB5jFmrwnrgLX0urIqR/1ptY18TzkqXm7M= +github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4 h1:QfbVf0bOIRMp/WHAWsuVDB7KHoWnRsGbvDuOf2ua7k4= +github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4/go.mod h1:Oq9KbiRNDBB5jFmrwnrgLX0urIqR/1ptY18TzkqXm7M= github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= diff --git a/octopusdeploy/data_source_tenants.go b/octopusdeploy/data_source_tenants.go deleted file mode 100644 index 4c9cfa569..000000000 --- a/octopusdeploy/data_source_tenants.go +++ /dev/null @@ -1,54 +0,0 @@ -package octopusdeploy - -import ( - "context" - "time" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceTenants() *schema.Resource { - return &schema.Resource{ - Description: "Provides information about existing tenants.", - ReadContext: dataSourceTenantsRead, - Schema: getTenantDataSchema(), - } -} - -func dataSourceTenantsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - query := tenants.TenantsQuery{ - ClonedFromTenantID: d.Get("cloned_from_tenant_id").(string), - IDs: expandArray(d.Get("ids").([]interface{})), - IsClone: d.Get("is_clone").(bool), - Name: d.Get("name").(string), - PartialName: d.Get("partial_name").(string), - ProjectID: d.Get("project_id").(string), - Skip: d.Get("skip").(int), - Tags: expandArray(d.Get("tags").([]interface{})), - Take: d.Get("take").(int), - } - - spaceID := d.Get("space_id").(string) - - client := meta.(*client.Client) - existingTenants, err := tenants.Get(client, spaceID, query) - if err != nil { - return diag.FromErr(err) - } - - flattenedTenants := []interface{}{} - for _, tenant := range existingTenants.Items { - flattenedTenants = append(flattenedTenants, flattenTenant(tenant)) - } - - if err := d.Set("tenants", flattenedTenants); err != nil { - return diag.FromErr(err) - } - - d.SetId("Tenants " + time.Now().UTC().String()) - - return nil -} diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index 07f503374..6de5657b1 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -2,7 +2,6 @@ package octopusdeploy import ( "context" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -30,7 +29,6 @@ func Provider() *schema.Provider { "octopusdeploy_ssh_connection_deployment_targets": dataSourceSSHConnectionDeploymentTargets(), "octopusdeploy_tag_sets": dataSourceTagSets(), "octopusdeploy_teams": dataSourceTeams(), - "octopusdeploy_tenants": dataSourceTenants(), "octopusdeploy_users": dataSourceUsers(), "octopusdeploy_user_roles": dataSourceUserRoles(), "octopusdeploy_worker_pools": dataSourceWorkerPools(), diff --git a/octopusdeploy/resource_deployment_process_test.go b/octopusdeploy/resource_deployment_process_test.go index d800b81da..10bdefef0 100644 --- a/octopusdeploy/resource_deployment_process_test.go +++ b/octopusdeploy/resource_deployment_process_test.go @@ -436,7 +436,6 @@ func testAccDeploymentProcessCheckDestroy(s *terraform.State) error { func TestDeploymentProcessWithGitDependency(t *testing.T) { testFramework := test.OctopusContainerTest{} - newSpaceId, err := testFramework.Act(t, octoContainer, "../terraform", "51-deploymentprocesswithgitdependency", []string{}) if err != nil { diff --git a/octopusdeploy/testing_container_test.go b/octopusdeploy/testing_container_test.go index 6fd5dfba7..1f37ad0a3 100644 --- a/octopusdeploy/testing_container_test.go +++ b/octopusdeploy/testing_container_test.go @@ -27,7 +27,7 @@ func TestMain(m *testing.M) { if *createSharedContainer { testFramework := test.OctopusContainerTest{} - octoContainer, octoClient, sqlServerContainer, network, err = testFramework.ArrangeContainer(m) + octoContainer, octoClient, sqlServerContainer, network, err = testFramework.ArrangeContainer() if err != nil { log.Printf("Failed to arrange containers: (%s)", err.Error()) } diff --git a/octopusdeploy_framework/datasource_environments.go b/octopusdeploy_framework/datasource_environments.go index b788107d5..06b52a527 100644 --- a/octopusdeploy_framework/datasource_environments.go +++ b/octopusdeploy_framework/datasource_environments.go @@ -45,14 +45,14 @@ func (*environmentDataSource) Schema(_ context.Context, req datasource.SchemaReq Attributes: map[string]schema.Attribute{ //request "ids": util.GetQueryIDsDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema(schemas.EnvironmentResourceDescription), + "space_id": schemas.GetSpaceIdDatasourceSchema(schemas.EnvironmentResourceDescription, false), "name": util.GetQueryNameDatasourceSchema(), "partial_name": util.GetQueryPartialNameDatasourceSchema(), "skip": util.GetQuerySkipDatasourceSchema(), "take": util.GetQueryTakeDatasourceSchema(), //response - "id": util.GetIdDatasourceSchema(), + "id": schemas.GetIdDatasourceSchema(true), }, Blocks: map[string]schema.Block{ "environments": schema.ListNestedBlock{ diff --git a/octopusdeploy_framework/datasource_project_groups.go b/octopusdeploy_framework/datasource_project_groups.go index 01713abbc..bca82cd2a 100644 --- a/octopusdeploy_framework/datasource_project_groups.go +++ b/octopusdeploy_framework/datasource_project_groups.go @@ -50,14 +50,14 @@ func (p *projectGroupsDataSource) Schema(_ context.Context, _ datasource.SchemaR resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ // request - "space_id": util.GetSpaceIdDatasourceSchema(description), + "space_id": schemas.GetSpaceIdDatasourceSchema(description, false), "ids": util.GetQueryIDsDatasourceSchema(), "partial_name": util.GetQueryPartialNameDatasourceSchema(), "skip": util.GetQuerySkipDatasourceSchema(), "take": util.GetQueryTakeDatasourceSchema(), // response - "id": util.GetIdDatasourceSchema(), + "id": schemas.GetIdDatasourceSchema(true), }, Blocks: map[string]schema.Block{ "project_groups": schema.ListNestedBlock{ diff --git a/octopusdeploy_framework/datasource_spaces.go b/octopusdeploy_framework/datasource_spaces.go index c7ec6414c..00ba2352c 100644 --- a/octopusdeploy_framework/datasource_spaces.go +++ b/octopusdeploy_framework/datasource_spaces.go @@ -42,7 +42,7 @@ func (*spacesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r "take": schemas.GetQueryTakeDatasourceSchema(), // response - "id": schemas.GetIdDatasourceSchema(), + "id": schemas.GetIdDatasourceSchema(true), }, Blocks: map[string]schema.Block{ "spaces": schema.ListNestedBlock{ diff --git a/octopusdeploy_framework/datasource_tenants.go b/octopusdeploy_framework/datasource_tenants.go new file mode 100644 index 000000000..bf8b2867a --- /dev/null +++ b/octopusdeploy_framework/datasource_tenants.go @@ -0,0 +1,80 @@ +package octopusdeploy_framework + +import ( + "context" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/datasource" + datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "time" +) + +type tenantsDataSource struct { + *Config +} + +func NewTenantsDataSource() datasource.DataSource { + return &tenantsDataSource{} +} + +func (*tenantsDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = util.GetTypeName("tenants") +} + +func (e *tenantsDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + e.Config = DataSourceConfiguration(req, resp) +} + +func (*tenantsDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = datasourceSchema.Schema{ + Description: "Provides information about existing tenants.", + Attributes: schemas.GetTenantsDataSourceSchema(), + Blocks: map[string]datasourceSchema.Block{ + "tenants": datasourceSchema.ListNestedBlock{ + Description: "A list of tenants that match the filter(s).", + NestedObject: datasourceSchema.NestedBlockObject{ + Attributes: schemas.GetTenantDataSourceSchema(), + }, + }, + }, + } +} + +func (b *tenantsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var err error + var data schemas.TenantsModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + query := tenants.TenantsQuery{ + ClonedFromTenantID: data.ClonedFromTenantId.ValueString(), + IDs: util.ExpandStringList(data.IDs), + IsClone: data.IsClone.ValueBool(), + Name: data.Name.ValueString(), + PartialName: data.PartialName.ValueString(), + ProjectID: data.ProjectId.ValueString(), + Skip: int(data.Skip.ValueInt64()), + Tags: util.ExpandStringList(data.Tags), + Take: int(data.Take.ValueInt64()), + } + + existingTenants, err := tenants.Get(b.Client, data.SpaceID.ValueString(), query) + if err != nil { + resp.Diagnostics.AddError("unable to load tenants", err.Error()) + return + } + + flattenedTenants := []interface{}{} + for _, tenant := range existingTenants.Items { + flattenedTenants = append(flattenedTenants, schemas.FlattenTenant(tenant)) + } + + data.ID = types.StringValue("Tenants " + time.Now().UTC().String()) + data.Tenants, _ = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: schemas.TenantObjectType()}, flattenedTenants) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/octopusdeploy/data_source_tenants_test.go b/octopusdeploy_framework/datasource_tenants_test.go similarity index 82% rename from octopusdeploy/data_source_tenants_test.go rename to octopusdeploy_framework/datasource_tenants_test.go index b7baa57c8..95907b087 100644 --- a/octopusdeploy/data_source_tenants_test.go +++ b/octopusdeploy_framework/datasource_tenants_test.go @@ -1,13 +1,12 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "strconv" "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccDataSourceTenants(t *testing.T) { @@ -17,7 +16,7 @@ func TestAccDataSourceTenants(t *testing.T) { take := acctest.RandIntRange(0, 100) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: ProtoV6ProviderFactories(), Steps: []resource.TestStep{ { diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index 447f66237..95bf6c0a1 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -2,9 +2,9 @@ package octopusdeploy_framework import ( "context" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "os" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" @@ -70,6 +70,7 @@ func (p *octopusDeployFrameworkProvider) DataSources(ctx context.Context) []func NewLibraryVariableSetDataSource, NewVariablesDataSource, NewProjectsDataSource, + NewTenantsDataSource, } } diff --git a/octopusdeploy_framework/schemas/environment.go b/octopusdeploy_framework/schemas/environment.go index 964532446..6ab91f49d 100644 --- a/octopusdeploy_framework/schemas/environment.go +++ b/octopusdeploy_framework/schemas/environment.go @@ -27,21 +27,20 @@ const ( func GetEnvironmentDatasourceSchema() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ - "id": util.GetIdDatasourceSchema(), - "slug": util.GetSlugDatasourceSchema(EnvironmentResourceDescription), - "name": util.GetNameDatasourceWithMaxLengthSchema(true, 50), + "id": GetIdDatasourceSchema(true), + "slug": util.GetSlugDatasourceSchema(EnvironmentResourceDescription, true), + "name": GetReadonlyNameDatasourceSchema(), "description": util.GetDescriptionDatasourceSchema(EnvironmentResourceDescription), EnvironmentSortOrder: util.GetSortOrderDataSourceSchema(EnvironmentResourceDescription), EnvironmentAllowDynamicInfrastructure: datasourceSchema.BoolAttribute{ - Optional: true, + Computed: true, }, EnvironmentUseGuidedFailure: datasourceSchema.BoolAttribute{ - Optional: true, + Computed: true, }, - "space_id": util.GetSpaceIdDatasourceSchema(EnvironmentResourceDescription), + "space_id": GetSpaceIdDatasourceSchema(EnvironmentResourceDescription, true), EnvironmentJiraExtensionSettings: datasourceSchema.ListNestedAttribute{ Description: "Provides extension settings for the Jira integration for this environment.", - Optional: true, Computed: true, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ @@ -62,7 +61,6 @@ func GetEnvironmentDatasourceSchema() map[string]datasourceSchema.Attribute { }, EnvironmentJiraServiceManagementExtensionSettings: datasourceSchema.ListNestedAttribute{ Description: "Provides extension settings for the Jira Service Management (JSM) integration for this environment.", - Optional: true, Computed: true, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ @@ -72,7 +70,6 @@ func GetEnvironmentDatasourceSchema() map[string]datasourceSchema.Attribute { }, EnvironmentServiceNowExtensionSettings: datasourceSchema.ListNestedAttribute{ Description: "Provides extension settings for the ServiceNow integration for this environment.", - Optional: true, Computed: true, NestedObject: datasourceSchema.NestedAttributeObject{ Attributes: map[string]datasourceSchema.Attribute{ diff --git a/octopusdeploy_framework/schemas/feed.go b/octopusdeploy_framework/schemas/feed.go index 60a084e64..61b01ae23 100644 --- a/octopusdeploy_framework/schemas/feed.go +++ b/octopusdeploy_framework/schemas/feed.go @@ -78,10 +78,10 @@ func GetFeedsDataSourceSchema() map[string]datasourceSchema.Attribute { "partial_name": util.GetQueryPartialNameDatasourceSchema(), "skip": util.GetQuerySkipDatasourceSchema(), "take": util.GetQueryTakeDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema("feeds"), + "space_id": GetSpaceIdDatasourceSchema("feeds", false), // response - "id": util.GetIdDatasourceSchema(), + "id": GetIdDatasourceSchema(true), } } @@ -89,7 +89,7 @@ func GetFeedDataSourceSchema() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ "feed_type": datasourceSchema.StringAttribute{ Description: "A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`.", - Optional: true, + Computed: true, Validators: []validator.String{ stringvalidator.OneOf( "AwsElasticContainerRegistry", @@ -103,17 +103,17 @@ func GetFeedDataSourceSchema() map[string]datasourceSchema.Attribute { }, }, "feed_uri": datasourceSchema.StringAttribute{ - Required: true, + Computed: true, }, - "id": util.GetIdDatasourceSchema(), + "id": GetIdDatasourceSchema(true), "is_enhanced_mode": datasourceSchema.BoolAttribute{ - Optional: true, + Computed: true, }, - "name": util.GetNameDatasourceSchema(true), + "name": GetReadonlyNameDatasourceSchema(), "password": datasourceSchema.StringAttribute{ Description: "The password associated with this resource.", Sensitive: true, - Optional: true, + Computed: true, Validators: []validator.String{ stringvalidator.LengthAtLeast(1), }, @@ -121,45 +121,42 @@ func GetFeedDataSourceSchema() map[string]datasourceSchema.Attribute { "package_acquisition_location_options": datasourceSchema.ListAttribute{ Computed: true, ElementType: types.StringType, - Optional: true, }, "region": datasourceSchema.StringAttribute{ Computed: true, }, "registry_path": datasourceSchema.StringAttribute{ - Optional: true, + Computed: true, }, "secret_key": datasourceSchema.StringAttribute{ - Optional: true, + Computed: true, Sensitive: true, }, - "space_id": util.GetSpaceIdDatasourceSchema("feeds"), + "space_id": GetSpaceIdDatasourceSchema("feeds", true), "username": datasourceSchema.StringAttribute{ Description: "The username associated with this resource.", Sensitive: true, - Optional: true, + Computed: true, Validators: []validator.String{ stringvalidator.LengthAtLeast(1), }, }, "delete_unreleased_packages_after_days": datasourceSchema.Int64Attribute{ - Optional: true, + Computed: true, }, "access_key": datasourceSchema.StringAttribute{ - Required: true, + Computed: true, Description: "The AWS access key to use when authenticating against Amazon Web Services.", }, "api_version": datasourceSchema.StringAttribute{ - Optional: true, + Computed: true, }, "download_attempts": datasourceSchema.Int64Attribute{ Description: "The number of times a deployment should attempt to download a package from this feed before failing.", - Optional: true, Computed: true, }, "download_retry_backoff_seconds": datasourceSchema.Int64Attribute{ Description: "The number of seconds to apply as a linear back off between download attempts.", - Optional: true, Computed: true, }, } diff --git a/octopusdeploy_framework/schemas/library_variable_set.go b/octopusdeploy_framework/schemas/library_variable_set.go index 3ca65b627..76ba84d14 100644 --- a/octopusdeploy_framework/schemas/library_variable_set.go +++ b/octopusdeploy_framework/schemas/library_variable_set.go @@ -42,8 +42,8 @@ func getLibraryVariableSetDataSchema() map[string]datasourceSchema.Attribute { Description: "A filter to search by content type.", Optional: true, }, - "id": util.GetIdDatasourceSchema(), - "space_id": util.GetSpaceIdDatasourceSchema("library variable set"), + "id": GetIdDatasourceSchema(true), + "space_id": GetSpaceIdDatasourceSchema("library variable set", false), "ids": util.GetQueryIDsDatasourceSchema(), "partial_name": util.GetQueryPartialNameDatasourceSchema(), "skip": util.GetQuerySkipDatasourceSchema(), @@ -53,16 +53,16 @@ func getLibraryVariableSetDataSchema() map[string]datasourceSchema.Attribute { func GetLibraryVariableSetObjectDatasourceSchema() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ - "description": GetDescriptionDatasourceSchema("library variable set"), - "id": GetIdDatasourceSchema(), - "name": GetNameDatasourceSchema(false), - "space_id": GetSpaceIdDatasourceSchema("library variable set"), + "description": GetReadonlyDescriptionDatasourceSchema("library variable set"), + "id": GetIdDatasourceSchema(true), + "name": GetReadonlyNameDatasourceSchema(), + "space_id": GetSpaceIdDatasourceSchema("library variable set", true), "template_ids": datasourceSchema.MapAttribute{ ElementType: types.StringType, Computed: true, }, "template": datasourceSchema.ListAttribute{ - Optional: true, + Computed: true, ElementType: types.ObjectType{AttrTypes: TemplateObjectType()}, }, "variable_set_id": datasourceSchema.StringAttribute{ diff --git a/octopusdeploy_framework/schemas/project_group.go b/octopusdeploy_framework/schemas/project_group.go index bafc5ea42..27b42acfd 100644 --- a/octopusdeploy_framework/schemas/project_group.go +++ b/octopusdeploy_framework/schemas/project_group.go @@ -15,7 +15,7 @@ func GetProjectGroupDatasourceSchema() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ "id": util.GetIdResourceSchema(), "space_id": util.GetSpaceIdResourceSchema(projectGroupDescription), - "name": util.GetNameResourceSchema(true), + "name": GetReadonlyNameDatasourceSchema(), "retention_policy_id": datasourceSchema.StringAttribute{ Computed: true, Optional: true, diff --git a/octopusdeploy_framework/schemas/schema.go b/octopusdeploy_framework/schemas/schema.go index 395aefc7f..496dbc913 100644 --- a/octopusdeploy_framework/schemas/schema.go +++ b/octopusdeploy_framework/schemas/schema.go @@ -5,7 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" - //"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -56,20 +55,41 @@ func GetQueryTakeDatasourceSchema() datasourceSchema.Attribute { } } -func GetIdDatasourceSchema() datasourceSchema.Attribute { +func GetReadonlyNameDatasourceSchema() datasourceSchema.Attribute { return datasourceSchema.StringAttribute{ - Description: "The unique ID for this resource.", + Description: "The name of this resource.", Computed: true, - Optional: true, } } -func GetSpaceIdDatasourceSchema(resourceDescription string) datasourceSchema.Attribute { - return datasourceSchema.StringAttribute{ +func GetIdDatasourceSchema(isReadOnly bool) datasourceSchema.Attribute { + s := datasourceSchema.StringAttribute{ + Description: "The unique ID for this resource.", + } + + if isReadOnly { + s.Computed = true + } else { + s.Computed = true + s.Optional = true + } + + return s +} + +func GetSpaceIdDatasourceSchema(resourceDescription string, isReadOnly bool) datasourceSchema.Attribute { + s := datasourceSchema.StringAttribute{ Description: "The space ID associated with this " + resourceDescription + ".", - Computed: true, - Optional: true, } + + if isReadOnly { + s.Computed = true + } else { + s.Computed = true + s.Optional = true + } + + return s } func GetNameDatasourceWithMaxLengthSchema(isRequired bool, maxLength int) datasourceSchema.Attribute { @@ -113,6 +133,13 @@ func GetDescriptionDatasourceSchema(resourceDescription string) datasourceSchema } } +func GetReadonlyDescriptionDatasourceSchema(resourceDescription string) datasourceSchema.Attribute { + return datasourceSchema.StringAttribute{ + Description: "The description of this " + resourceDescription + ".", + Computed: true, + } +} + func GetIdResourceSchema() resourceSchema.Attribute { return resourceSchema.StringAttribute{ Description: "The unique ID for this resource.", @@ -155,12 +182,19 @@ func GetDescriptionResourceSchema(resourceDescription string) resourceSchema.Att } } -func GetSlugDatasourceSchema(resourceDescription string) datasourceSchema.Attribute { - return datasourceSchema.StringAttribute{ +func GetSlugDatasourceSchema(resourceDescription string, isReadOnly bool) datasourceSchema.Attribute { + s := datasourceSchema.StringAttribute{ Description: fmt.Sprintf("The unique slug of this %s", resourceDescription), - Optional: true, - Computed: true, } + + if isReadOnly { + s.Computed = true + } else { + s.Optional = true + s.Computed = true + } + + return s } func GetSlugResourceSchema(resourceDescription string) resourceSchema.Attribute { diff --git a/octopusdeploy_framework/schemas/space.go b/octopusdeploy_framework/schemas/space.go index 662a95c96..0c1159553 100644 --- a/octopusdeploy_framework/schemas/space.go +++ b/octopusdeploy_framework/schemas/space.go @@ -1,10 +1,13 @@ package schemas import ( + "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -57,29 +60,33 @@ func GetSpaceResourceSchema() map[string]resourceSchema.Attribute { func GetSpaceDatasourceSchema() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ - "id": GetIdDatasourceSchema(), - "description": GetDescriptionDatasourceSchema(spaceDescription), - "name": GetNameDatasourceWithMaxLengthSchema(true, 20), - "slug": GetSlugDatasourceSchema(spaceDescription), + "id": GetIdDatasourceSchema(true), + "description": GetReadonlyDescriptionDatasourceSchema(spaceDescription), + "name": datasourceSchema.StringAttribute{ + Description: fmt.Sprintf("The name of this resource, no more than %d characters long", 20), + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 20), + }, + Required: true, + }, + "slug": GetSlugDatasourceSchema(spaceDescription, true), "space_managers_teams": datasourceSchema.SetAttribute{ ElementType: types.StringType, Description: "A list of team IDs designated to be managers of this space.", - Optional: true, Computed: true, }, "space_managers_team_members": datasourceSchema.SetAttribute{ ElementType: types.StringType, Description: "A list of user IDs designated to be managers of this space.", - Optional: true, Computed: true, }, "is_task_queue_stopped": datasourceSchema.BoolAttribute{ Description: "Specifies the status of the task queue for this space.", - Optional: true, + Computed: true, }, "is_default": datasourceSchema.BoolAttribute{ Description: "Specifies if this space is the default space in Octopus.", - Optional: true, + Computed: true, }, } } diff --git a/octopusdeploy_framework/schemas/tenant.go b/octopusdeploy_framework/schemas/tenant.go new file mode 100644 index 000000000..41d55b7d1 --- /dev/null +++ b/octopusdeploy_framework/schemas/tenant.go @@ -0,0 +1,107 @@ +package schemas + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/attr" + datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type TenantModel struct { + ClonedFromTenantId types.String `tfsdk:"cloned_from_tenant_id"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + SpaceID types.String `tfsdk:"space_id"` + TenantTags types.List `tfsdk:"tenant_tags"` +} + +type TenantsModel struct { + ClonedFromTenantId types.String `tfsdk:"cloned_from_tenant_id"` + ID types.String `tfsdk:"id"` + IDs types.List `tfsdk:"ids"` + IsClone types.Bool `tfsdk:"is_clone"` + Name types.String `tfsdk:"name"` + PartialName types.String `tfsdk:"partial_name"` + ProjectId types.String `tfsdk:"project_id"` + Skip types.Int64 `tfsdk:"skip"` + Tags types.List `tfsdk:"tags"` + SpaceID types.String `tfsdk:"space_id"` + Tenants types.List `tfsdk:"tenants"` + Take types.Int64 `tfsdk:"take"` +} + +func TenantObjectType() map[string]attr.Type { + return map[string]attr.Type{ + "cloned_from_tenant_id": types.StringType, + "description": types.StringType, + "id": types.StringType, + "name": types.StringType, + "space_id": types.StringType, + "tenant_tags": types.ListType{ElemType: types.StringType}, + } +} + +func FlattenTenant(tenant *tenants.Tenant) attr.Value { + tenantTags := make([]attr.Value, len(tenant.TenantTags)) + for i, value := range tenant.TenantTags { + tenantTags[i] = types.StringValue(value) + } + var tenantTagsList, _ = types.ListValue(types.StringType, tenantTags) + + return types.ObjectValueMust(TenantObjectType(), map[string]attr.Value{ + "cloned_from_tenant_id": types.StringValue(tenant.ClonedFromTenantID), + "description": types.StringValue(tenant.Description), + "id": types.StringValue(tenant.GetID()), + "name": types.StringValue(tenant.Name), + "space_id": types.StringValue(tenant.SpaceID), + "tenant_tags": tenantTagsList, + }) +} + +func GetTenantsDataSourceSchema() map[string]datasourceSchema.Attribute { + return map[string]datasourceSchema.Attribute{ + "cloned_from_tenant_id": datasourceSchema.StringAttribute{ + Description: "A filter to search for a cloned tenant by its ID.", + Optional: true, + }, + "id": GetIdDatasourceSchema(true), + "ids": util.GetQueryIDsDatasourceSchema(), + "is_clone": datasourceSchema.BoolAttribute{ + Description: "A filter to search for cloned resources.", + Optional: true, + }, + "name": datasourceSchema.StringAttribute{ + Description: "A filter to search by name.", + Optional: true, + }, + "partial_name": util.GetQueryPartialNameDatasourceSchema(), + "project_id": datasourceSchema.StringAttribute{ + Description: "A filter to search by a project ID.", + Optional: true, + }, + "skip": util.GetQuerySkipDatasourceSchema(), + "tags": util.GetQueryDatasourceTags(), + "space_id": GetSpaceIdDatasourceSchema("tenants", false), + "take": util.GetQueryTakeDatasourceSchema(), + } +} + +func GetTenantDataSourceSchema() map[string]datasourceSchema.Attribute { + return map[string]datasourceSchema.Attribute{ + "cloned_from_tenant_id": datasourceSchema.StringAttribute{ + Description: "The ID of the tenant from which this tenant was cloned.", + Computed: true, + }, + "description": util.GetDescriptionDatasourceSchema("tenants"), + "id": GetIdDatasourceSchema(true), + "name": GetReadonlyNameDatasourceSchema(), + "space_id": GetSpaceIdDatasourceSchema("tenant", true), + "tenant_tags": datasourceSchema.ListAttribute{ + Computed: true, + Description: "A list of tenant tags associated with this resource.", + ElementType: types.StringType, + }, + } +} diff --git a/octopusdeploy_framework/testing_container_test.go b/octopusdeploy_framework/testing_container_test.go index a876440fd..49319d02e 100644 --- a/octopusdeploy_framework/testing_container_test.go +++ b/octopusdeploy_framework/testing_container_test.go @@ -28,7 +28,7 @@ func TestMain(m *testing.M) { if *createSharedContainer { testFramework := test.OctopusContainerTest{} - octoContainer, octoClient, sqlServerContainer, network, err = testFramework.ArrangeContainer(m) + octoContainer, octoClient, sqlServerContainer, network, err = testFramework.ArrangeContainer() if err != nil { log.Printf("Failed to arrange containers: (%s)", err.Error()) } diff --git a/octopusdeploy_framework/util/schema.go b/octopusdeploy_framework/util/schema.go index 433892367..d8108b803 100644 --- a/octopusdeploy_framework/util/schema.go +++ b/octopusdeploy_framework/util/schema.go @@ -60,22 +60,6 @@ func GetQueryTakeDatasourceSchema() datasourceSchema.Attribute { } } -func GetIdDatasourceSchema() datasourceSchema.Attribute { - return datasourceSchema.StringAttribute{ - Description: "The unique ID for this resource.", - Computed: true, - Optional: true, - } -} - -func GetSpaceIdDatasourceSchema(resourceDescription string) datasourceSchema.Attribute { - return datasourceSchema.StringAttribute{ - Description: "The space ID associated with this " + resourceDescription + ".", - Computed: true, - Optional: true, - } -} - func GetNameDatasourceWithMaxLengthSchema(isRequired bool, maxLength int) datasourceSchema.Attribute { s := datasourceSchema.StringAttribute{ Description: fmt.Sprintf("The name of this resource, no more than %d characters long", maxLength), @@ -117,6 +101,14 @@ func GetDescriptionDatasourceSchema(resourceDescription string) datasourceSchema } } +func GetQueryDatasourceTags() datasourceSchema.Attribute { + return datasourceSchema.ListAttribute{ + Description: "A filter to search by a list of tags.", + ElementType: types.StringType, + Optional: true, + } +} + func GetIdResourceSchema() resourceSchema.Attribute { return resourceSchema.StringAttribute{ Description: "The unique ID for this resource.", @@ -165,12 +157,19 @@ func GetDescriptionResourceSchema(resourceDescription string) resourceSchema.Att } } -func GetSlugDatasourceSchema(resourceDescription string) resourceSchema.Attribute { - return resourceSchema.StringAttribute{ +func GetSlugDatasourceSchema(resourceDescription string, isReadOnly bool) datasourceSchema.Attribute { + s := datasourceSchema.StringAttribute{ Description: fmt.Sprintf("The unique slug of this %s", resourceDescription), - Optional: true, - Computed: true, } + + if isReadOnly { + s.Computed = true + } else { + s.Optional = true + s.Computed = true + } + + return s } func GetSlugResourceSchema(resourceDescription string) resourceSchema.Attribute { @@ -184,7 +183,6 @@ func GetSlugResourceSchema(resourceDescription string) resourceSchema.Attribute func GetSortOrderDataSourceSchema(resourceDescription string) resourceSchema.Attribute { return resourceSchema.Int64Attribute{ Description: fmt.Sprintf("The order number to sort an %s", resourceDescription), - Optional: true, Computed: true, } } From e5533e062c96133ee8e1c084bf7a77cea5cea18e Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Fri, 9 Aug 2024 00:55:57 -0700 Subject: [PATCH 07/12] chore: update environment resource and datasource docs (#708) --- docs/resources/environment.md | 17 ++++--- .../schemas/environment.go | 51 ++++++++++++++----- octopusdeploy_framework/util/schema.go | 3 +- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/docs/resources/environment.md b/docs/resources/environment.md index c9f9c5d1e..22b847d57 100644 --- a/docs/resources/environment.md +++ b/docs/resources/environment.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_environment Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - + This resource manages environments in Octopus Deploy. --- # octopusdeploy_environment (Resource) - +This resource manages environments in Octopus Deploy. ## Example Usage @@ -48,17 +48,20 @@ resource "octopusdeploy_environment" "example" { - `jira_extension_settings` (Block List) Provides extension settings for the Jira integration for this environment. (see [below for nested schema](#nestedblock--jira_extension_settings)) - `jira_service_management_extension_settings` (Block List) Provides extension settings for the Jira Service Management (JSM) integration for this environment. (see [below for nested schema](#nestedblock--jira_service_management_extension_settings)) - `servicenow_extension_settings` (Block List) Provides extension settings for the ServiceNow integration for this environment. (see [below for nested schema](#nestedblock--servicenow_extension_settings)) -- `slug` (String) The unique slug of this environment -- `sort_order` (Number) The order number to sort an environment +- `sort_order` (Number) The order number to sort an environment. - `space_id` (String) The space ID associated with this environment. - `use_guided_failure` (Boolean) +### Read-Only + +- `slug` (String) The unique slug of this environment + ### Nested Schema for `jira_extension_settings` Optional: -- `environment_type` (String) +- `environment_type` (String) The Jira environment type of this Octopus deployment environment. Valid values are `"development"`, `"production"`, `"staging"`, `"testing"`, `"unmapped"`. @@ -66,7 +69,7 @@ Optional: Optional: -- `is_enabled` (Boolean) +- `is_enabled` (Boolean) Specifies whether or not this extension is enabled for this project. @@ -74,7 +77,7 @@ Optional: Optional: -- `is_enabled` (Boolean) +- `is_enabled` (Boolean) Specifies whether or not this extension is enabled for this project. ## Import diff --git a/octopusdeploy_framework/schemas/environment.go b/octopusdeploy_framework/schemas/environment.go index 6ab91f49d..ed57bba65 100644 --- a/octopusdeploy_framework/schemas/environment.go +++ b/octopusdeploy_framework/schemas/environment.go @@ -1,6 +1,9 @@ package schemas import ( + "fmt" + "strings" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments" "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -25,6 +28,28 @@ const ( EnvironmentServiceNowExtensionSettingsIsEnabled = "is_enabled" ) +var jiraEnvironmentTypeNames = struct { + Development string + Production string + Testing string + Staging string + Unmapped string +}{ + Development: "development", + Production: "production", + Testing: "testing", + Staging: "staging", + Unmapped: "unmapped", +} + +var jiraEnvironmentTypes = []string{ + jiraEnvironmentTypeNames.Development, + jiraEnvironmentTypeNames.Production, + jiraEnvironmentTypeNames.Staging, + jiraEnvironmentTypeNames.Testing, + jiraEnvironmentTypeNames.Unmapped, +} + func GetEnvironmentDatasourceSchema() map[string]datasourceSchema.Attribute { return map[string]datasourceSchema.Attribute{ "id": GetIdDatasourceSchema(true), @@ -48,11 +73,7 @@ func GetEnvironmentDatasourceSchema() map[string]datasourceSchema.Attribute { Computed: true, Validators: []validator.String{ stringvalidator.OneOfCaseInsensitive( - "development", - "production", - "testing", - "staging", - "unmapped", + jiraEnvironmentTypes..., ), }, }, @@ -82,6 +103,7 @@ func GetEnvironmentDatasourceSchema() map[string]datasourceSchema.Attribute { func GetEnvironmentResourceSchema() resourceSchema.Schema { return resourceSchema.Schema{ + Description: util.GetResourceSchemaDescription(EnvironmentResourceDescription), Attributes: map[string]resourceSchema.Attribute{ "id": util.GetIdResourceSchema(), "slug": util.GetSlugResourceSchema(EnvironmentResourceDescription), @@ -106,14 +128,11 @@ func GetEnvironmentResourceSchema() resourceSchema.Schema { NestedObject: resourceSchema.NestedBlockObject{ Attributes: map[string]resourceSchema.Attribute{ "environment_type": resourceSchema.StringAttribute{ - Optional: true, + Description: fmt.Sprintf("The Jira environment type of this Octopus deployment environment. Valid values are %s.", strings.Join(util.Map(jiraEnvironmentTypes, func(item string) string { return fmt.Sprintf("`\"%s\"`", item) }), ", ")), + Optional: true, Validators: []validator.String{ stringvalidator.OneOfCaseInsensitive( - "development", - "production", - "staging", - "testing", - "unmapped", + jiraEnvironmentTypes..., ), }, }, @@ -124,7 +143,10 @@ func GetEnvironmentResourceSchema() resourceSchema.Schema { Description: "Provides extension settings for the Jira Service Management (JSM) integration for this environment.", NestedObject: resourceSchema.NestedBlockObject{ Attributes: map[string]resourceSchema.Attribute{ - "is_enabled": resourceSchema.BoolAttribute{Optional: true}, + "is_enabled": resourceSchema.BoolAttribute{ + Description: "Specifies whether or not this extension is enabled for this project.", + Optional: true, + }, }, }, }, @@ -132,7 +154,10 @@ func GetEnvironmentResourceSchema() resourceSchema.Schema { Description: "Provides extension settings for the ServiceNow integration for this environment.", NestedObject: resourceSchema.NestedBlockObject{ Attributes: map[string]resourceSchema.Attribute{ - "is_enabled": resourceSchema.BoolAttribute{Optional: true}, + "is_enabled": resourceSchema.BoolAttribute{ + Description: "Specifies whether or not this extension is enabled for this project.", + Optional: true, + }, }, }, }, diff --git a/octopusdeploy_framework/util/schema.go b/octopusdeploy_framework/util/schema.go index d8108b803..26856a9da 100644 --- a/octopusdeploy_framework/util/schema.go +++ b/octopusdeploy_framework/util/schema.go @@ -175,7 +175,6 @@ func GetSlugDatasourceSchema(resourceDescription string, isReadOnly bool) dataso func GetSlugResourceSchema(resourceDescription string) resourceSchema.Attribute { return resourceSchema.StringAttribute{ Description: fmt.Sprintf("The unique slug of this %s", resourceDescription), - Optional: true, Computed: true, } } @@ -189,7 +188,7 @@ func GetSortOrderDataSourceSchema(resourceDescription string) resourceSchema.Att func GetSortOrderResourceSchema(resourceDescription string) resourceSchema.Attribute { return resourceSchema.Int64Attribute{ - Description: fmt.Sprintf("The order number to sort an %s", resourceDescription), + Description: fmt.Sprintf("The order number to sort an %s.", resourceDescription), Optional: true, Computed: true, } From 04b191762f5773a3b62386be37391d58dc51021b Mon Sep 17 00:00:00 2001 From: Isaac Calligeros <101079287+IsaacCalligeros95@users.noreply.github.com> Date: Mon, 12 Aug 2024 10:35:34 +0930 Subject: [PATCH 08/12] Chore!: Require updated docs (#697) * Adds a GHA to require no docs differences between generated and checked in --- .github/workflows/docs.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..2e2328067 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,19 @@ +name: Validate Docs +on: + push: + branches: + - '**' + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-go@v3 + with: + go-version: '1.22' + - run: go generate main.go + - name: Check for file changes + run: git diff --exit-code From c595415a729f483b16006da13967325bdc5fc900 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 11 Aug 2024 20:38:43 -0700 Subject: [PATCH 09/12] chore!: migrate runbook resource to tf framework (#709) --- docs/resources/runbook.md | 10 +- octopusdeploy/provider.go | 1 - octopusdeploy/resource_runbook.go | 104 ------- octopusdeploy/resource_runbook_process.go | 3 +- octopusdeploy/schema_runbook.go | 176 ----------- .../schema_runbook_retention_period.go | 50 --- octopusdeploy_framework/framework_provider.go | 1 + octopusdeploy_framework/resource_project.go | 2 +- octopusdeploy_framework/resource_runbook.go | 179 +++++++++++ .../resource_runbook_migration_test.go | 121 ++++++++ .../resource_runbook_test.go | 7 +- .../schemas/connectivity_policy.go | 142 +++++++++ octopusdeploy_framework/schemas/runbook.go | 288 ++++++++++++++++++ .../schemas/runbook_retention_period.go | 99 ++++++ octopusdeploy_framework/util/logging.go | 25 ++ 15 files changed, 867 insertions(+), 341 deletions(-) delete mode 100644 octopusdeploy/resource_runbook.go delete mode 100644 octopusdeploy/schema_runbook.go delete mode 100644 octopusdeploy/schema_runbook_retention_period.go create mode 100644 octopusdeploy_framework/resource_runbook.go create mode 100644 octopusdeploy_framework/resource_runbook_migration_test.go rename octopusdeploy/resource_runbook_process_test.go => octopusdeploy_framework/resource_runbook_test.go (99%) create mode 100644 octopusdeploy_framework/schemas/connectivity_policy.go create mode 100644 octopusdeploy_framework/schemas/runbook.go create mode 100644 octopusdeploy_framework/schemas/runbook_retention_period.go diff --git a/docs/resources/runbook.md b/docs/resources/runbook.md index b540fd6e7..09b12e7b3 100644 --- a/docs/resources/runbook.md +++ b/docs/resources/runbook.md @@ -22,16 +22,16 @@ This resource manages runbooks in Octopus Deploy. ### Optional -- `connectivity_policy` (Block List, Max: 1) (see [below for nested schema](#nestedblock--connectivity_policy)) +- `connectivity_policy` (Block List) (see [below for nested schema](#nestedblock--connectivity_policy)) - `default_guided_failure_mode` (String) Sets the runbook guided failure mode. - `description` (String) The description of this runbook. - `environment_scope` (String) Determines how the runbook is scoped to environments. - `environments` (List of String) When environment_scope is set to "Specified", this is the list of environments the runbook can be run against. -- `force_package_download` (Boolean) Whether to force packages to be re-downloaded or not +- `force_package_download` (Boolean) Whether to force packages to be re-downloaded or not. - `id` (String) The unique ID for this resource. -- `multi_tenancy_mode` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`. -- `retention_policy` (Block List, Max: 1) Sets the runbook retention policy (see [below for nested schema](#nestedblock--retention_policy)) -- `space_id` (String) The space ID associated with this resource. +- `multi_tenancy_mode` (String) The tenanted deployment mode of the runbook. Valid modes are `Untenanted`, `TenantedOrUntenanted`, `Tenanted` +- `retention_policy` (Block List) Sets the runbook retention policy. (see [below for nested schema](#nestedblock--retention_policy)) +- `space_id` (String) The space ID associated with this runbook. ### Read-Only diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index 6de5657b1..794853fff 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -58,7 +58,6 @@ func Provider() *schema.Provider { "octopusdeploy_project_deployment_target_trigger": resourceProjectDeploymentTargetTrigger(), "octopusdeploy_external_feed_create_release_trigger": resourceExternalFeedCreateReleaseTrigger(), "octopusdeploy_project_scheduled_trigger": resourceProjectScheduledTrigger(), - "octopusdeploy_runbook": resourceRunbook(), "octopusdeploy_runbook_process": resourceRunbookProcess(), "octopusdeploy_scoped_user_role": resourceScopedUserRole(), "octopusdeploy_script_module": resourceScriptModule(), diff --git a/octopusdeploy/resource_runbook.go b/octopusdeploy/resource_runbook.go deleted file mode 100644 index 7497a1a04..000000000 --- a/octopusdeploy/resource_runbook.go +++ /dev/null @@ -1,104 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceRunbook() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceRunbookCreate, - DeleteContext: resourceRunbookDelete, - Description: "This resource manages runbooks in Octopus Deploy.", - Importer: getImporter(), - ReadContext: resourceRunbookRead, - Schema: getRunbookSchema(), - UpdateContext: resourceRunbookUpdate, - } -} - -func resourceRunbookCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - runbook := expandRunbook(ctx, d) - - tflog.Info(ctx, fmt.Sprintf("creating runbook (%s)", runbook.Name)) - - client := m.(*client.Client) - createdRunbook, err := runbooks.Add(client, runbook) - if err != nil { - return diag.FromErr(err) - } - - if err := setRunbook(ctx, d, createdRunbook); err != nil { - return diag.FromErr(err) - } - - d.SetId(createdRunbook.GetID()) - - tflog.Info(ctx, fmt.Sprintf("runbook created (%s)", d.Id())) - return nil -} - -func resourceRunbookDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, fmt.Sprintf("deleting runbook (%s)", d.Id())) - - client := m.(*client.Client) - if err := runbooks.DeleteByID(client, d.Get("space_id").(string), d.Id()); err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("runbook deleted (%s)", d.Id())) - d.SetId("") - return nil -} - -func resourceRunbookRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, fmt.Sprintf("reading runbook (%s)", d.Id())) - - client := m.(*client.Client) - runbook, err := runbooks.GetByID(client, d.Get("space_id").(string), d.Id()) - if err != nil { - return errors.ProcessApiError(ctx, d, err, "runbook") - } - - if err := setRunbook(ctx, d, runbook); err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("runbook read (%s)", d.Id())) - return nil -} - -func resourceRunbookUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Info(ctx, fmt.Sprintf("updating runbook (%s)", d.Id())) - - client := m.(*client.Client) - runbook := expandRunbook(ctx, d) - var updatedRunbook *runbooks.Runbook - var err error - - runbookLinks, err := runbooks.GetByID(client, d.Get("space_id").(string), d.Id()) - if err != nil { - return diag.FromErr(err) - } - - runbook.Links = runbookLinks.Links - - updatedRunbook, err = runbooks.Update(client, runbook) - if err != nil { - return diag.FromErr(err) - } - - if err := setRunbook(ctx, d, updatedRunbook); err != nil { - return diag.FromErr(err) - } - - tflog.Info(ctx, fmt.Sprintf("runbook updated (%s)", d.Id())) - return nil -} diff --git a/octopusdeploy/resource_runbook_process.go b/octopusdeploy/resource_runbook_process.go index 5ff0fb3ac..8019bd4b5 100644 --- a/octopusdeploy/resource_runbook_process.go +++ b/octopusdeploy/resource_runbook_process.go @@ -105,7 +105,8 @@ func resourceRunbookProcessDelete(ctx context.Context, d *schema.ResourceData, m } runbookProcess := &runbookprocess.RunbookProcess{ - Version: current.Version, + ProjectID: current.ProjectID, + Version: current.Version, } runbookProcess.Links = current.Links runbookProcess.ID = d.Id() diff --git a/octopusdeploy/schema_runbook.go b/octopusdeploy/schema_runbook.go deleted file mode 100644 index 47ffebba4..000000000 --- a/octopusdeploy/schema_runbook.go +++ /dev/null @@ -1,176 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func expandRunbook(ctx context.Context, d *schema.ResourceData) *runbooks.Runbook { - name := d.Get("name").(string) - projectId := d.Get("project_id").(string) - - runbook := runbooks.NewRunbook(name, projectId) - runbook.ID = d.Id() - - if v, ok := d.GetOk("description"); ok { - runbook.Description = v.(string) - } - - if v, ok := d.GetOk("runbook_process_id"); ok { - runbook.RunbookProcessID = v.(string) - } - - if v, ok := d.GetOk("published_runbook_snapshot_id"); ok { - runbook.PublishedRunbookSnapshotID = v.(string) - } - - if v, ok := d.GetOk("space_id"); ok { - runbook.SpaceID = v.(string) - } - - if v, ok := d.GetOk("multi_tenancy_mode"); ok { - runbook.MultiTenancyMode = core.TenantedDeploymentMode(v.(string)) - } - - if v, ok := d.GetOk("connectivity_policy"); ok { - runbook.ConnectivityPolicy = expandConnectivityPolicy(v.([]interface{})) - } - - if v, ok := d.GetOk("environment_scope"); ok { - runbook.EnvironmentScope = v.(string) - } - - if v, ok := d.GetOk("environments"); ok { - runbook.Environments = getSliceFromTerraformTypeList(v) - } - - if v, ok := d.GetOk("default_guided_failure_mode"); ok { - runbook.DefaultGuidedFailureMode = v.(string) - } - - if v, ok := d.GetOk("retention_policy"); ok { - runbook.RunRetentionPolicy = expandRunbookRetentionPolicy(v.([]interface{})) - } - - if v, ok := d.GetOk("force_package_download"); ok { - runbook.ForcePackageDownload = v.(bool) - } - - return runbook -} - -func getRunbookSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "id": getIDSchema(), - "name": { - Description: "The name of the runbook in Octopus Deploy. This name must be unique.", - Required: true, - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringIsNotWhiteSpace), - }, - "description": { - Computed: true, - Description: "The description of this runbook.", - Optional: true, - Type: schema.TypeString, - }, - "project_id": { - Description: "The project that this runbook belongs to.", - Required: true, - Type: schema.TypeString, - }, - "runbook_process_id": { - Description: "The runbook process ID.", - Computed: true, - Type: schema.TypeString, - }, - "published_runbook_snapshot_id": { - Description: "The published snapshot ID.", - Computed: true, - Type: schema.TypeString, - }, - "space_id": getSpaceIDSchema(), - "multi_tenancy_mode": getTenantedDeploymentSchema(), - "connectivity_policy": { - Computed: true, - Elem: &schema.Resource{Schema: getConnectivityPolicySchema()}, - MaxItems: 1, - Optional: true, - Type: schema.TypeList, - }, - "environment_scope": { - Description: "Determines how the runbook is scoped to environments.", - Computed: true, - Optional: true, - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ - "All", - "Specified", - "FromProjectLifecycles", - }, false)), - }, - "environments": { - Description: "When environment_scope is set to \"Specified\", this is the list of environments the runbook can be run against.", - Computed: true, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Type: schema.TypeList, - }, - "default_guided_failure_mode": { - Description: "Sets the runbook guided failure mode.", - Computed: true, - Optional: true, - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ - "EnvironmentDefault", - "Off", - "On", - }, false)), - }, - "retention_policy": { - Description: "Sets the runbook retention policy", - Computed: true, - DefaultFunc: func() (interface{}, error) { - return flattenRunbookRetentionPeriod(&runbooks.RunbookRetentionPeriod{ - QuantityToKeep: 100, - ShouldKeepForever: false, - }), nil - }, - Elem: &schema.Resource{Schema: getRunbookRetentionPeriodSchema()}, - MaxItems: 1, - Optional: true, - Type: schema.TypeList, - }, - "force_package_download": { - Description: "Whether to force packages to be re-downloaded or not", - Computed: true, - Optional: true, - Type: schema.TypeBool, - }, - } -} - -func setRunbook(ctx context.Context, d *schema.ResourceData, runbook *runbooks.Runbook) error { - d.Set("id", runbook.GetID()) - d.Set("name", runbook.Name) - d.Set("project_id", runbook.ProjectID) - d.Set("description", runbook.Description) - d.Set("runbook_process_id", runbook.RunbookProcessID) - d.Set("published_runbook_snapshot_id", runbook.PublishedRunbookSnapshotID) - d.Set("space_id", runbook.SpaceID) - d.Set("multi_tenancy_mode", runbook.MultiTenancyMode) - if err := d.Set("connectivity_policy", flattenConnectivityPolicy(runbook.ConnectivityPolicy)); err != nil { - return fmt.Errorf("error setting connectivity_policy: %s", err) - } - d.Set("environment_scope", runbook.EnvironmentScope) - d.Set("environments", runbook.Environments) - d.Set("default_guided_failure_mode", runbook.DefaultGuidedFailureMode) - d.Set("force_package_download", runbook.ForcePackageDownload) - - return nil -} diff --git a/octopusdeploy/schema_runbook_retention_period.go b/octopusdeploy/schema_runbook_retention_period.go deleted file mode 100644 index 516cada68..000000000 --- a/octopusdeploy/schema_runbook_retention_period.go +++ /dev/null @@ -1,50 +0,0 @@ -package octopusdeploy - -import ( - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func expandRunbookRetentionPolicy(flattenedRetentionPeriod interface{}) *runbooks.RunbookRetentionPeriod { - if flattenedRetentionPeriod == nil { - return nil - } - - retentionPeriodProperties := flattenedRetentionPeriod.([]interface{}) - if len(retentionPeriodProperties) == 1 { - retentionPeriodMap := retentionPeriodProperties[0].(map[string]interface{}) - return &runbooks.RunbookRetentionPeriod{ - QuantityToKeep: int32(retentionPeriodMap["quantity_to_keep"].(int)), - ShouldKeepForever: retentionPeriodMap["should_keep_forever"].(bool), - } - } - - return nil -} - -func flattenRunbookRetentionPeriod(r *runbooks.RunbookRetentionPeriod) []interface{} { - retentionPeriod := make(map[string]interface{}) - retentionPeriod["quantity_to_keep"] = int(r.QuantityToKeep) - retentionPeriod["should_keep_forever"] = r.ShouldKeepForever - return []interface{}{retentionPeriod} -} - -func getRunbookRetentionPeriodSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "quantity_to_keep": { - Default: 100, - Description: "How many runs to keep per environment.", - Optional: true, - Type: schema.TypeInt, - ValidateDiagFunc: validation.ToDiagFunc(validation.IntAtLeast(0)), - }, - "should_keep_forever": { - Default: false, - Description: "Indicates if items should never be deleted. The default value is `false`.", - Optional: true, - Type: schema.TypeBool, - ConflictsWith: []string{"retention_policy.quantity_to_keep"}, - }, - } -} diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index 95bf6c0a1..fc1128666 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -94,6 +94,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func() NewVariableResource, NewProjectResource, NewDockerContainerRegistryFeedResource, + NewRunbookResource, } } diff --git a/octopusdeploy_framework/resource_project.go b/octopusdeploy_framework/resource_project.go index bcefbbf69..7a8ece169 100644 --- a/octopusdeploy_framework/resource_project.go +++ b/octopusdeploy_framework/resource_project.go @@ -94,7 +94,7 @@ func (r *projectResource) Read(ctx context.Context, req resource.ReadRequest, re project, err := projects.GetByID(r.Client, state.SpaceID.ValueString(), state.ID.ValueString()) if err != nil { - if err := errors.ProcessApiErrorV2(ctx, resp, state, err, "lifecycle"); err != nil { + if err := errors.ProcessApiErrorV2(ctx, resp, state, err, "project"); err != nil { resp.Diagnostics.AddError("Error reading project", err.Error()) } return diff --git a/octopusdeploy_framework/resource_runbook.go b/octopusdeploy_framework/resource_runbook.go new file mode 100644 index 000000000..066f32b13 --- /dev/null +++ b/octopusdeploy_framework/resource_runbook.go @@ -0,0 +1,179 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +var _ resource.ResourceWithImportState = &runbookTypeResource{} + +type runbookTypeResource struct { + *Config +} + +func NewRunbookResource() resource.Resource { + return &runbookTypeResource{} +} + +func (*runbookTypeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = util.GetTypeName(schemas.RunbookResourceDescription) +} + +func (*runbookTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schemas.GetRunbookResourceSchema() +} + +func (r *runbookTypeResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.Config = ResourceConfiguration(req, resp) +} + +func (r *runbookTypeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan schemas.RunbookTypeResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + name := plan.Name.ValueString() + projectId := plan.ProjectID.ValueString() + + runbook := runbooks.NewRunbook(name, projectId) + if !plan.ID.IsNull() { + runbook.ID = plan.ID.ValueString() + } + + runbook.Description = plan.Description.ValueString() + runbook.RunbookProcessID = plan.RunbookProcessID.ValueString() + runbook.PublishedRunbookSnapshotID = plan.PublishedRunbookSnapshotID.ValueString() + runbook.SpaceID = plan.SpaceID.ValueString() + if !plan.MultiTenancyMode.IsNull() { + runbook.MultiTenancyMode = core.TenantedDeploymentMode(plan.MultiTenancyMode.ValueString()) + } + runbook.ConnectivityPolicy = schemas.MapToConnectivityPolicy(plan.ConnectivityPolicy) + runbook.EnvironmentScope = plan.EnvironmentScope.ValueString() + runbook.Environments = util.ExpandStringList(plan.Environments) + runbook.DefaultGuidedFailureMode = plan.DefaultGuidedFailureMode.ValueString() + runbook.RunRetentionPolicy = schemas.MapToRunbookRetentionPeriod(plan.RunRetentionPolicy) + runbook.ForcePackageDownload = plan.ForcePackageDownload.ValueBool() + + util.Create(ctx, schemas.RunbookResourceDescription, plan) + + createdRunbook, err := runbooks.Add(r.Config.Client, runbook) + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("failed to create runbook (%s)", runbook.Name), err.Error()) + return + } + + resp.Diagnostics.Append(plan.RefreshFromApiResponse(ctx, createdRunbook)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + + util.Created(ctx, schemas.RunbookResourceDescription, createdRunbook) +} + +func (r *runbookTypeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state schemas.RunbookTypeResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + util.Reading(ctx, schemas.RunbookResourceDescription, state) + + runbook, err := runbooks.GetByID(r.Config.Client, state.SpaceID.ValueString(), state.ID.ValueString()) + if err != nil { + if err := errors.ProcessApiErrorV2(ctx, resp, state, err, schemas.RunbookResourceDescription); err != nil { + resp.Diagnostics.AddError("failed to load runbook", err.Error()) + } + return + } + + resp.Diagnostics.Append(state.RefreshFromApiResponse(ctx, runbook)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + + util.Read(ctx, schemas.RunbookResourceDescription, runbook) +} + +func (r *runbookTypeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state schemas.RunbookTypeResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + util.Update(ctx, schemas.RunbookResourceDescription, plan) + + runbook, err := runbooks.GetByID(r.Config.Client, state.SpaceID.ValueString(), state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("unable to load runbook", err.Error()) + return + } + + updatedRunbook := runbooks.NewRunbook(plan.Name.ValueString(), plan.ProjectID.ValueString()) + updatedRunbook.ID = runbook.GetID() + updatedRunbook.SpaceID = runbook.SpaceID + updatedRunbook.Description = plan.Description.ValueString() + updatedRunbook.RunbookProcessID = plan.RunbookProcessID.ValueString() + updatedRunbook.PublishedRunbookSnapshotID = plan.PublishedRunbookSnapshotID.ValueString() + if !plan.MultiTenancyMode.IsNull() { + updatedRunbook.MultiTenancyMode = core.TenantedDeploymentMode(plan.MultiTenancyMode.ValueString()) + } + updatedRunbook.ConnectivityPolicy = schemas.MapToConnectivityPolicy(plan.ConnectivityPolicy) + updatedRunbook.EnvironmentScope = plan.EnvironmentScope.ValueString() + updatedRunbook.Environments = util.ExpandStringList(plan.Environments) + updatedRunbook.DefaultGuidedFailureMode = plan.DefaultGuidedFailureMode.ValueString() + updatedRunbook.RunRetentionPolicy = schemas.MapToRunbookRetentionPeriod(plan.RunRetentionPolicy) + updatedRunbook.ForcePackageDownload = plan.ForcePackageDownload.ValueBool() + + updatedRunbook, err = runbooks.Update(r.Config.Client, updatedRunbook) + if err != nil { + resp.Diagnostics.AddError("failed to update runbook", err.Error()) + } + + resp.Diagnostics.Append(plan.RefreshFromApiResponse(ctx, updatedRunbook)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + + util.Updated(ctx, schemas.RunbookResourceDescription, updatedRunbook) +} + +func (*runbookTypeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func (r *runbookTypeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state schemas.RunbookTypeResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + util.Delete(ctx, schemas.RunbookResourceDescription, state) + + if err := runbooks.DeleteByID(r.Config.Client, state.SpaceID.ValueString(), state.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("failed to delete runbook", err.Error()) + return + } + + util.Deleted(ctx, schemas.RunbookResourceDescription, state) + resp.State.RemoveResource(ctx) +} diff --git a/octopusdeploy_framework/resource_runbook_migration_test.go b/octopusdeploy_framework/resource_runbook_migration_test.go new file mode 100644 index 000000000..3eb66928a --- /dev/null +++ b/octopusdeploy_framework/resource_runbook_migration_test.go @@ -0,0 +1,121 @@ +package octopusdeploy_framework + +import ( + "fmt" + "os" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" + internaltest "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/test" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/stretchr/testify/assert" +) + +func TestRunbookResource_UpgradeFromSDK_ToPluginFramework(t *testing.T) { + internaltest.SkipCI(t, "'octopusdeploy_runbook.runbook1' - expected NoOp, got action(s): [update]") + os.Setenv("TF_CLI_CONFIG_FILE=", "") + + name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + resource.Test(t, resource.TestCase{ + CheckDestroy: testRunbookDestroyed, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "octopusdeploy": { + VersionConstraint: "0.22.0", + Source: "OctopusDeployLabs/octopusdeploy", + }, + }, + Config: runbookConfig(name), + }, + { + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Config: runbookConfig(name), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("octopusdeploy_runbook.runbook1", "NoOp"), + }, + }, + }, + { + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Config: updatedRunbookConfig(name), + Check: resource.ComposeTestCheckFunc( + testRunbookUpdated(t, name), + ), + }, + }, + }) +} + +func runbookConfig(name string) string { + return fmt.Sprintf(` + resource "octopusdeploy_project" "project1" { + name = "project %[1]s" + lifecycle_id = "Lifecycles-1" + project_group_id = "ProjectGroups-1" + } + resource "octopusdeploy_runbook" "runbook1" { + project_id = octopusdeploy_project.project1.id + name = "runbook %[1]s" + } + `, name) +} + +func updatedRunbookConfig(name string) string { + return fmt.Sprintf(` + resource "octopusdeploy_project" "project1" { + name = "project %[1]s" + lifecycle_id = "Lifecycles-1" + project_group_id = "ProjectGroups-1" + } + resource "octopusdeploy_runbook" "runbook1" { + project_id = octopusdeploy_project.project1.id + name = "runbook %[1]s" + description = "description %[1]s" + connectivity_policy { + allow_deployments_to_no_targets = true + exclude_unhealthy_targets = true + } + retention_policy { + quantity_to_keep = 10 + } + } + `, name) +} + +func testRunbookDestroyed(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "octopusdeploy_runbook" { + runbook, err := runbooks.GetByID(octoClient, octoClient.GetSpaceID(), rs.Primary.ID) + if err == nil && runbook != nil { + return fmt.Errorf("runbook (%s) still exists", rs.Primary.ID) + } + } + } + + return nil +} + +func testRunbookUpdated(t *testing.T, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + projectId := s.RootModule().Resources["octopusdeploy_project.project1"].Primary.ID + runbookId := s.RootModule().Resources["octopusdeploy_runbook.runbook1"].Primary.ID + runbook, err := runbooks.GetByID(octoClient, octoClient.GetSpaceID(), runbookId) + if err != nil { + return fmt.Errorf("failed to retrieve runbook by ID: %s", err) + } + + assert.NotEmpty(t, runbook.ID, "Runbook ID did not match expected value") + assert.Equal(t, runbook.ProjectID, projectId) + assert.Equal(t, runbook.Description, fmt.Sprintf("description %s", name)) + assert.True(t, runbook.ConnectivityPolicy.AllowDeploymentsToNoTargets, "allow_deployments_to_no_targets should be true") + assert.True(t, runbook.ConnectivityPolicy.ExcludeUnhealthyTargets, "exclude_unhealthy_targets should be true") + assert.Equal(t, runbook.RunRetentionPolicy.QuantityToKeep, 10) + + return nil + } +} diff --git a/octopusdeploy/resource_runbook_process_test.go b/octopusdeploy_framework/resource_runbook_test.go similarity index 99% rename from octopusdeploy/resource_runbook_process_test.go rename to octopusdeploy_framework/resource_runbook_test.go index 0f1de80d0..992b38238 100644 --- a/octopusdeploy/resource_runbook_process_test.go +++ b/octopusdeploy_framework/resource_runbook_test.go @@ -1,11 +1,12 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" - "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/octoclient" - "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/test" "strings" "testing" + + "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/octoclient" + "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/test" ) // TestRunbookResource verifies that a runbook can be reimported with the correct settings diff --git a/octopusdeploy_framework/schemas/connectivity_policy.go b/octopusdeploy_framework/schemas/connectivity_policy.go new file mode 100644 index 000000000..508718f3b --- /dev/null +++ b/octopusdeploy_framework/schemas/connectivity_policy.go @@ -0,0 +1,142 @@ +package schemas + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var runbookConnectivityPolicySchemeAttributeNames = struct { + AllowDeploymentsToNoTargets string + ExcludeUnhealthyTargets string + SkipMachineBehavior string + TargetRoles string +}{ + AllowDeploymentsToNoTargets: "allow_deployments_to_no_targets", + ExcludeUnhealthyTargets: "exclude_unhealthy_targets", + SkipMachineBehavior: "skip_machine_behavior", + TargetRoles: "target_roles", +} + +var skipMachineBehaviorNames = struct { + SkipUnavailableMachines string + None string +}{ + SkipUnavailableMachines: "SkipUnavailableMachines", + None: "None", +} + +var skipMachineBehaviors = []string{ + skipMachineBehaviorNames.SkipUnavailableMachines, + skipMachineBehaviorNames.None, +} + +func GetConnectivityPolicyObjectType() map[string]attr.Type { + return map[string]attr.Type{ + runbookConnectivityPolicySchemeAttributeNames.AllowDeploymentsToNoTargets: types.BoolType, + runbookConnectivityPolicySchemeAttributeNames.ExcludeUnhealthyTargets: types.BoolType, + runbookConnectivityPolicySchemeAttributeNames.SkipMachineBehavior: types.StringType, + runbookConnectivityPolicySchemeAttributeNames.TargetRoles: types.ListType{ElemType: types.StringType}, + } +} + +func getConnectivityPolicySchema() map[string]resourceSchema.Attribute { + return map[string]resourceSchema.Attribute{ + runbookConnectivityPolicySchemeAttributeNames.AllowDeploymentsToNoTargets: resourceSchema.BoolAttribute{ + Computed: true, + Optional: true, + Default: booldefault.StaticBool(true), + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + runbookConnectivityPolicySchemeAttributeNames.ExcludeUnhealthyTargets: resourceSchema.BoolAttribute{ + Computed: true, + Optional: true, + Default: booldefault.StaticBool(false), + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + runbookConnectivityPolicySchemeAttributeNames.SkipMachineBehavior: resourceSchema.StringAttribute{ + Computed: true, + Optional: true, + Default: stringdefault.StaticString(skipMachineBehaviorNames.None), + Validators: []validator.String{ + stringvalidator.OneOf( + skipMachineBehaviors..., + ), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + runbookConnectivityPolicySchemeAttributeNames.TargetRoles: resourceSchema.ListAttribute{ + Computed: true, + Optional: true, + ElementType: types.StringType, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + }, + } +} + +func GetDefaultConnectivityPolicy() *core.ConnectivityPolicy { + return &core.ConnectivityPolicy{ + AllowDeploymentsToNoTargets: true, + ExcludeUnhealthyTargets: false, + SkipMachineBehavior: core.SkipMachineBehaviorNone, + TargetRoles: []string{}, + } +} + +func MapFromConnectivityPolicy(connectivityPolicy *core.ConnectivityPolicy) attr.Value { + if connectivityPolicy == nil { + return MapFromConnectivityPolicy(GetDefaultConnectivityPolicy()) + } + + attrs := map[string]attr.Value{ + runbookConnectivityPolicySchemeAttributeNames.AllowDeploymentsToNoTargets: types.BoolValue(connectivityPolicy.AllowDeploymentsToNoTargets), + runbookConnectivityPolicySchemeAttributeNames.ExcludeUnhealthyTargets: types.BoolValue(connectivityPolicy.ExcludeUnhealthyTargets), + runbookConnectivityPolicySchemeAttributeNames.SkipMachineBehavior: types.StringValue(string(connectivityPolicy.SkipMachineBehavior)), + runbookConnectivityPolicySchemeAttributeNames.TargetRoles: util.FlattenStringList(connectivityPolicy.TargetRoles), + } + + return types.ObjectValueMust(GetConnectivityPolicyObjectType(), attrs) +} + +func MapToConnectivityPolicy(flattenedConnectivityPolicy types.List) *core.ConnectivityPolicy { + if flattenedConnectivityPolicy.IsNull() || len(flattenedConnectivityPolicy.Elements()) == 0 { + return GetDefaultConnectivityPolicy() + } + + obj := flattenedConnectivityPolicy.Elements()[0].(types.Object) + attrs := obj.Attributes() + + var connectivityPolicy core.ConnectivityPolicy + if allowDeploymentsToNoTargets, ok := attrs[runbookConnectivityPolicySchemeAttributeNames.AllowDeploymentsToNoTargets].(types.Bool); ok && !allowDeploymentsToNoTargets.IsNull() { + connectivityPolicy.AllowDeploymentsToNoTargets = allowDeploymentsToNoTargets.ValueBool() + } + if excludeUnhealthyTargets, ok := attrs[runbookConnectivityPolicySchemeAttributeNames.ExcludeUnhealthyTargets].(types.Bool); ok && !excludeUnhealthyTargets.IsNull() { + connectivityPolicy.ExcludeUnhealthyTargets = excludeUnhealthyTargets.ValueBool() + } + if skipMachineBehavior, ok := attrs[runbookConnectivityPolicySchemeAttributeNames.SkipMachineBehavior].(types.String); ok && !skipMachineBehavior.IsNull() { + connectivityPolicy.SkipMachineBehavior = core.SkipMachineBehavior(skipMachineBehavior.ValueString()) + } + if targetRoles, ok := attrs[runbookConnectivityPolicySchemeAttributeNames.TargetRoles].(types.List); ok && !targetRoles.IsNull() { + connectivityPolicy.TargetRoles = util.ExpandStringList(targetRoles) + } + + return &connectivityPolicy +} diff --git a/octopusdeploy_framework/schemas/runbook.go b/octopusdeploy_framework/schemas/runbook.go new file mode 100644 index 000000000..2bc493d94 --- /dev/null +++ b/octopusdeploy_framework/schemas/runbook.go @@ -0,0 +1,288 @@ +package schemas + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +const RunbookResourceDescription = "runbook" + +var RunbookSchemaAttributeNames = struct { + ID string + Name string + Description string + ProjectID string + RunbookProcessID string + PublishedRunbookSnapshotID string + SpaceID string + MultiTenancyMode string + ConnectivityPolicy string + EnvironmentScope string + Environments string + DefaultGuidedFailureMode string + RetentionPolicy string + ForcePackageDownload string +}{ + ID: "id", + Name: "name", + Description: "description", + ProjectID: "project_id", + RunbookProcessID: "runbook_process_id", + PublishedRunbookSnapshotID: "published_runbook_snapshot_id", + SpaceID: "space_id", + MultiTenancyMode: "multi_tenancy_mode", + ConnectivityPolicy: "connectivity_policy", + EnvironmentScope: "environment_scope", + Environments: "environments", + DefaultGuidedFailureMode: "default_guided_failure_mode", + RetentionPolicy: "retention_policy", + ForcePackageDownload: "force_package_download", +} + +var tenantedDeploymentModeNames = struct { + Untenanted string + TenantedOrUntenanted string + Tenanted string +}{ + Untenanted: "Untenanted", + TenantedOrUntenanted: "TenantedOrUntenanted", + Tenanted: "Tenanted", +} + +var tenantedDeploymentModes = []string{ + tenantedDeploymentModeNames.Untenanted, + tenantedDeploymentModeNames.TenantedOrUntenanted, + tenantedDeploymentModeNames.Tenanted, +} + +var environmentScopeNames = struct { + All string + Specified string + FromProjectLifecycles string +}{ + All: "All", + Specified: "Specified", + FromProjectLifecycles: "FromProjectLifecycles", +} + +var environmentScopeTypes = []string{ + environmentScopeNames.All, + environmentScopeNames.Specified, + environmentScopeNames.FromProjectLifecycles, +} + +var defaultGuidedFailureModeNames = struct { + EnvironmentDefault string + Off string + On string +}{ + EnvironmentDefault: "EnvironmentDefault", + Off: "Off", + On: "On", +} + +var defaultGuidedFailureModes = []string{ + defaultGuidedFailureModeNames.EnvironmentDefault, + defaultGuidedFailureModeNames.Off, + defaultGuidedFailureModeNames.On, +} + +type RunbookTypeResourceModel struct { + Name types.String `tfsdk:"name"` + ProjectID types.String `tfsdk:"project_id"` + Description types.String `tfsdk:"description"` + RunbookProcessID types.String `tfsdk:"runbook_process_id"` + PublishedRunbookSnapshotID types.String `tfsdk:"published_runbook_snapshot_id"` + SpaceID types.String `tfsdk:"space_id"` + MultiTenancyMode types.String `tfsdk:"multi_tenancy_mode"` + ConnectivityPolicy types.List `tfsdk:"connectivity_policy"` + EnvironmentScope types.String `tfsdk:"environment_scope"` + Environments types.List `tfsdk:"environments"` + DefaultGuidedFailureMode types.String `tfsdk:"default_guided_failure_mode"` + RunRetentionPolicy types.List `tfsdk:"retention_policy"` + ForcePackageDownload types.Bool `tfsdk:"force_package_download"` + + ResourceModel +} + +type RunbookRetentionPeriodModel struct { + QuantityToKeep types.String `tfsdk:"quantity_to_keep"` + ShouldKeepForever types.Bool `tfsdk:"should_keep_forever"` +} + +type RunbookConnectivityPolicyModel struct { + AllowDeploymentsToNoTargets types.Bool `tfsdk:"allow_deployments_to_no_targets"` + ExcludeUnhealthyTargets types.Bool `tfsdk:"exclude_unhealthy_targets"` + SkipMachineBehavior types.String `tfsdk:"skip_machine_behaviour"` + TargetRoles types.List `tfsdk:"target_roles"` +} + +func GetRunbookResourceSchema() resourceSchema.Schema { + return resourceSchema.Schema{ + Description: util.GetResourceSchemaDescription(RunbookResourceDescription), + Attributes: map[string]resourceSchema.Attribute{ + RunbookSchemaAttributeNames.ID: util.GetIdResourceSchema(), + RunbookSchemaAttributeNames.Name: resourceSchema.StringAttribute{ + Description: "The name of the runbook in Octopus Deploy. This name must be unique.", + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`\S+`), + "expected value to not be an empty string or whitespace", + ), + }, + }, + RunbookSchemaAttributeNames.Description: util.GetDescriptionResourceSchema(RunbookResourceDescription), + RunbookSchemaAttributeNames.ProjectID: resourceSchema.StringAttribute{ + Description: "The project that this runbook belongs to.", + Required: true, + }, + RunbookSchemaAttributeNames.RunbookProcessID: resourceSchema.StringAttribute{ + Description: "The runbook process ID.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + RunbookSchemaAttributeNames.PublishedRunbookSnapshotID: resourceSchema.StringAttribute{ + Description: "The published snapshot ID.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + RunbookSchemaAttributeNames.SpaceID: util.GetSpaceIdResourceSchema(RunbookResourceDescription), + RunbookSchemaAttributeNames.MultiTenancyMode: resourceSchema.StringAttribute{ + Description: fmt.Sprintf("The tenanted deployment mode of the runbook. Valid modes are %s", strings.Join(util.Map(tenantedDeploymentModes, func(item string) string { return fmt.Sprintf("`%s`", item) }), ", ")), + Computed: true, + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf(tenantedDeploymentModes...), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + RunbookSchemaAttributeNames.EnvironmentScope: resourceSchema.StringAttribute{ + Description: "Determines how the runbook is scoped to environments.", + Computed: true, + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf(environmentScopeTypes...), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + RunbookSchemaAttributeNames.Environments: resourceSchema.ListAttribute{ + Description: fmt.Sprintf("When %s is set to \"%s\", this is the list of environments the runbook can be run against.", RunbookSchemaAttributeNames.EnvironmentScope, environmentScopeNames.Specified), + Optional: true, + Computed: true, + ElementType: types.StringType, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + }, + RunbookSchemaAttributeNames.DefaultGuidedFailureMode: resourceSchema.StringAttribute{ + Description: "Sets the runbook guided failure mode.", + Optional: true, + Computed: true, + Validators: []validator.String{ + stringvalidator.OneOf(defaultGuidedFailureModes...), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + RunbookSchemaAttributeNames.ForcePackageDownload: resourceSchema.BoolAttribute{ + Description: "Whether to force packages to be re-downloaded or not.", + Computed: true, + Optional: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + }, + Blocks: map[string]resourceSchema.Block{ + RunbookSchemaAttributeNames.ConnectivityPolicy: resourceSchema.ListNestedBlock{ + NestedObject: resourceSchema.NestedBlockObject{ + Attributes: getConnectivityPolicySchema(), + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + RunbookSchemaAttributeNames.RetentionPolicy: resourceSchema.ListNestedBlock{ + Description: "Sets the runbook retention policy.", + NestedObject: resourceSchema.NestedBlockObject{ + Attributes: getRunbookRetentionPeriodSchema(), + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + }, + } +} + +func (data *RunbookTypeResourceModel) RefreshFromApiResponse(ctx context.Context, runbook *runbooks.Runbook) diag.Diagnostics { + var diags diag.Diagnostics + + if runbook == nil { + return diags + } + + data.ID = types.StringValue(runbook.ID) + data.Name = types.StringValue(runbook.Name) + data.ProjectID = types.StringValue(runbook.ProjectID) + data.Description = types.StringValue(runbook.Description) + data.RunbookProcessID = types.StringValue(runbook.RunbookProcessID) + data.PublishedRunbookSnapshotID = types.StringValue(runbook.PublishedRunbookSnapshotID) + data.SpaceID = types.StringValue(runbook.SpaceID) + data.MultiTenancyMode = types.StringValue(string(runbook.MultiTenancyMode)) + data.EnvironmentScope = types.StringValue(runbook.EnvironmentScope) + data.Environments = util.FlattenStringList(runbook.Environments) + data.DefaultGuidedFailureMode = types.StringValue(runbook.DefaultGuidedFailureMode) + data.ForcePackageDownload = types.BoolValue(runbook.ForcePackageDownload) + if !data.ConnectivityPolicy.IsNull() { + result, d := types.ListValueFrom( + ctx, + types.ObjectType{AttrTypes: GetConnectivityPolicyObjectType()}, + []attr.Value{MapFromConnectivityPolicy(runbook.ConnectivityPolicy)}, + ) + diags.Append(d...) + data.ConnectivityPolicy = result + } /*else { + data.ConnectivityPolicy = types.ListValueMust( + types.ObjectType{AttrTypes: GetConnectivityPolicyObjectType()}, + []attr.Value{MapFromConnectivityPolicy(GetDefaultConnectivityPolicy())}, + ) + }*/ + if !data.RunRetentionPolicy.IsNull() { + result, d := types.ListValueFrom( + ctx, + types.ObjectType{AttrTypes: GetRunbookRetentionPeriodObjectType()}, + []attr.Value{MapFromRunbookRetentionPeriod(runbook.RunRetentionPolicy)}, + ) + diags.Append(d...) + data.RunRetentionPolicy = result + } + + return diags +} diff --git a/octopusdeploy_framework/schemas/runbook_retention_period.go b/octopusdeploy_framework/schemas/runbook_retention_period.go new file mode 100644 index 000000000..d8bbdc8b6 --- /dev/null +++ b/octopusdeploy_framework/schemas/runbook_retention_period.go @@ -0,0 +1,99 @@ +package schemas + +import ( + "fmt" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" + "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var runbookRetentionPeriodSchemeAttributeNames = struct { + QuantityToKeep string + ShouldKeepForever string +}{ + QuantityToKeep: "quantity_to_keep", + ShouldKeepForever: "should_keep_forever", +} + +func GetRunbookRetentionPeriodObjectType() map[string]attr.Type { + return map[string]attr.Type{ + runbookRetentionPeriodSchemeAttributeNames.QuantityToKeep: types.Int64Type, + runbookRetentionPeriodSchemeAttributeNames.ShouldKeepForever: types.BoolType, + } +} + +func getRunbookRetentionPeriodSchema() map[string]resourceSchema.Attribute { + return map[string]resourceSchema.Attribute{ + runbookRetentionPeriodSchemeAttributeNames.QuantityToKeep: resourceSchema.Int64Attribute{ + Description: "How many runs to keep per environment.", + Computed: true, + Optional: true, + Validators: []validator.Int64{ + int64validator.AtLeast(0), + }, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + }, + runbookRetentionPeriodSchemeAttributeNames.ShouldKeepForever: resourceSchema.BoolAttribute{ + Description: "Indicates if items should never be deleted. The default value is `false`.", + Computed: true, + Optional: true, + Default: booldefault.StaticBool(false), + Validators: []validator.Bool{ + boolvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName(runbookRetentionPeriodSchemeAttributeNames.QuantityToKeep)), + }, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + } +} + +func GetDefaultRunbookRetentionPeriod() *runbooks.RunbookRetentionPeriod { + return &runbooks.RunbookRetentionPeriod{ + QuantityToKeep: 100, + ShouldKeepForever: false, + } +} + +func MapFromRunbookRetentionPeriod(retentionPeriod *runbooks.RunbookRetentionPeriod) attr.Value { + if retentionPeriod == nil { + return MapFromRunbookRetentionPeriod(GetDefaultRunbookRetentionPeriod()) + } + + attrs := map[string]attr.Value{ + runbookRetentionPeriodSchemeAttributeNames.QuantityToKeep: types.Int64Value(int64(retentionPeriod.QuantityToKeep)), + runbookRetentionPeriodSchemeAttributeNames.ShouldKeepForever: types.BoolValue(retentionPeriod.ShouldKeepForever), + } + + return types.ObjectValueMust(GetRunbookRetentionPeriodObjectType(), attrs) +} + +func MapToRunbookRetentionPeriod(flattenedRunbookRetentionPeriod types.List) *runbooks.RunbookRetentionPeriod { + if flattenedRunbookRetentionPeriod.IsNull() || len(flattenedRunbookRetentionPeriod.Elements()) == 0 { + return GetDefaultRunbookRetentionPeriod() + } + obj := flattenedRunbookRetentionPeriod.Elements()[0].(types.Object) + attrs := obj.Attributes() + + var runbookRetentionPeriod runbooks.RunbookRetentionPeriod + if quantityToKeep, ok := attrs[runbookRetentionPeriodSchemeAttributeNames.QuantityToKeep].(types.Int64); ok && !quantityToKeep.IsNull() { + runbookRetentionPeriod.QuantityToKeep = int32(quantityToKeep.ValueInt64()) + } + if shouldKeepForever, ok := attrs[runbookRetentionPeriodSchemeAttributeNames.ShouldKeepForever].(types.Bool); ok && !shouldKeepForever.IsNull() { + runbookRetentionPeriod.ShouldKeepForever = shouldKeepForever.ValueBool() + } + fmt.Printf("runbook retention period: %#v", runbookRetentionPeriod) + return &runbookRetentionPeriod +} diff --git a/octopusdeploy_framework/util/logging.go b/octopusdeploy_framework/util/logging.go index 139d7932d..66b3ca311 100644 --- a/octopusdeploy_framework/util/logging.go +++ b/octopusdeploy_framework/util/logging.go @@ -3,6 +3,7 @@ package util import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -13,3 +14,27 @@ func Create(ctx context.Context, resource string, v ...any) { func Created(ctx context.Context, resource string, v ...any) { tflog.Info(ctx, fmt.Sprintf("created %s: %#v", resource, v)) } + +func Delete(ctx context.Context, resource string, v ...any) { + tflog.Info(ctx, fmt.Sprintf("deleting %s: %#v", resource, v)) +} + +func Deleted(ctx context.Context, resource string, v ...any) { + tflog.Info(ctx, fmt.Sprintf("deleted %s: %#v", resource, v)) +} + +func Reading(ctx context.Context, resource string, v ...any) { + tflog.Info(ctx, fmt.Sprintf("reading %s: %#v", resource, v)) +} + +func Read(ctx context.Context, resource string, v ...any) { + tflog.Info(ctx, fmt.Sprintf("read %s: %#v", resource, v)) +} + +func Update(ctx context.Context, resource string, v ...any) { + tflog.Info(ctx, fmt.Sprintf("updating %s: %#v", resource, v)) +} + +func Updated(ctx context.Context, resource string, v ...any) { + tflog.Info(ctx, fmt.Sprintf("updated %s: %#v", resource, v)) +} From be6e0ecbc40204f589722263e49e681a6984f160 Mon Sep 17 00:00:00 2001 From: Isaac Calligeros <101079287+IsaacCalligeros95@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:27:53 +0930 Subject: [PATCH 10/12] Chore!: migrate tenant resource and datasource (#707) * Migrate tenants resource --- docs/resources/tenant.md | 2 +- octopusdeploy/provider.go | 1 - octopusdeploy/resource_tenant.go | 108 ----------- octopusdeploy/schema_tenant.go | 104 ----------- octopusdeploy/schema_tenant_test.go | 40 ---- octopusdeploy_framework/framework_provider.go | 1 + octopusdeploy_framework/resource_tenant.go | 174 ++++++++++++++++++ .../resource_tenant_migration_test.go | 109 +++++++++++ .../resource_tenant_test.go | 11 +- octopusdeploy_framework/schemas/tenant.go | 31 +++- 10 files changed, 320 insertions(+), 261 deletions(-) delete mode 100644 octopusdeploy/resource_tenant.go delete mode 100644 octopusdeploy/schema_tenant.go delete mode 100644 octopusdeploy/schema_tenant_test.go create mode 100644 octopusdeploy_framework/resource_tenant.go create mode 100644 octopusdeploy_framework/resource_tenant_migration_test.go rename {octopusdeploy => octopusdeploy_framework}/resource_tenant_test.go (95%) diff --git a/docs/resources/tenant.md b/docs/resources/tenant.md index ba3ce6aa6..16851becb 100644 --- a/docs/resources/tenant.md +++ b/docs/resources/tenant.md @@ -21,7 +21,7 @@ This resource manages tenants in Octopus Deploy. - `cloned_from_tenant_id` (String) The ID of the tenant from which this tenant was cloned. - `description` (String) The description of this tenant. - `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 tenant. - `tenant_tags` (List of String) A list of tenant tags associated with this resource. ~> **NOTE property `project_environment` deprecated:** The `project_environment` property has been replaced by the `octopusdeploy_tenant_project` resource to allow more advanced provisioning scenarioes. \ No newline at end of file diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index 794853fff..ebdaf0b94 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -67,7 +67,6 @@ func Provider() *schema.Provider { "octopusdeploy_tag": resourceTag(), "octopusdeploy_tag_set": resourceTagSet(), "octopusdeploy_team": resourceTeam(), - "octopusdeploy_tenant": resourceTenant(), "octopusdeploy_tentacle_certificate": resourceTentacleCertificate(), "octopusdeploy_token_account": resourceTokenAccount(), "octopusdeploy_user": resourceUser(), diff --git a/octopusdeploy/resource_tenant.go b/octopusdeploy/resource_tenant.go deleted file mode 100644 index 899cf6f48..000000000 --- a/octopusdeploy/resource_tenant.go +++ /dev/null @@ -1,108 +0,0 @@ -package octopusdeploy - -import ( - "context" - "log" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceTenant() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceTenantCreate, - DeleteContext: resourceTenantDelete, - Description: "This resource manages tenants in Octopus Deploy.", - Importer: getImporter(), - ReadContext: resourceTenantRead, - Schema: getTenantSchema(), - UpdateContext: resourceTenantUpdate, - } -} - -func resourceTenantCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - internal.Mutex.Lock() - defer internal.Mutex.Unlock() - - tenant := expandTenant(d) - - log.Printf("[INFO] creating tenant: %#v", tenant) - - client := m.(*client.Client) - createdTenant, err := tenants.Add(client, tenant) - if err != nil { - return diag.FromErr(err) - } - - if err := setTenant(ctx, d, createdTenant); err != nil { - return diag.FromErr(err) - } - - d.SetId(createdTenant.GetID()) - - log.Printf("[INFO] tenant created (%s)", d.Id()) - return nil -} - -func resourceTenantDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - internal.Mutex.Lock() - defer internal.Mutex.Unlock() - - log.Printf("[INFO] deleting tenant (%s)", d.Id()) - - client := m.(*client.Client) - if err := tenants.DeleteByID(client, d.Get("space_id").(string), d.Id()); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] tenant deleted (%s)", d.Id()) - d.SetId("") - return nil -} - -func resourceTenantRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] reading tenant (%s)", d.Id()) - - client := m.(*client.Client) - tenant, err := tenants.GetByID(client, d.Get("space_id").(string), d.Id()) - if err != nil { - return errors.ProcessApiError(ctx, d, err, "tenant") - } - - if err := setTenant(ctx, d, tenant); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] tenant read (%s)", d.Id()) - return nil -} - -func resourceTenantUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - internal.Mutex.Lock() - defer internal.Mutex.Unlock() - - log.Printf("[INFO] updating tenant (%s)", d.Id()) - - client := m.(*client.Client) - tenantFromApi, err := tenants.GetByID(client, d.Get("space_id").(string), d.Id()) - - tenant := expandTenant(d) - - // the project environments are not managed here, so we need to maintain the collection when updating - tenant.ProjectEnvironments = tenantFromApi.ProjectEnvironments - updatedTenant, err := tenants.Update(client, tenant) - if err != nil { - return diag.FromErr(err) - } - - if err := setTenant(ctx, d, updatedTenant); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] tenant updated (%s)", d.Id()) - return nil -} diff --git a/octopusdeploy/schema_tenant.go b/octopusdeploy/schema_tenant.go deleted file mode 100644 index 260ba93f8..000000000 --- a/octopusdeploy/schema_tenant.go +++ /dev/null @@ -1,104 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func expandTenant(d *schema.ResourceData) *tenants.Tenant { - name := d.Get("name").(string) - - tenant := tenants.NewTenant(name) - tenant.ID = d.Id() - - if v, ok := d.GetOk("cloned_from_tenant_id"); ok { - tenant.ClonedFromTenantID = v.(string) - } - - if v, ok := d.GetOk("description"); ok { - tenant.Description = v.(string) - } - - if v, ok := d.GetOk("space_id"); ok { - tenant.SpaceID = v.(string) - } - - if v, ok := d.GetOk("tenant_tags"); ok { - tenant.TenantTags = getSliceFromTerraformTypeList(v) - } - - return tenant -} - -func flattenTenant(tenant *tenants.Tenant) map[string]interface{} { - if tenant == nil { - return nil - } - - return map[string]interface{}{ - "cloned_from_tenant_id": tenant.ClonedFromTenantID, - "description": tenant.Description, - "id": tenant.GetID(), - "name": tenant.Name, - "space_id": tenant.SpaceID, - "tenant_tags": tenant.TenantTags, - } -} - -func getTenantDataSchema() map[string]*schema.Schema { - dataSchema := getTenantSchema() - setDataSchema(&dataSchema) - - return map[string]*schema.Schema{ - "cloned_from_tenant_id": getQueryClonedFromTenantID(), - "id": getDataSchemaID(), - "ids": getQueryIDs(), - "is_clone": getQueryIsClone(), - "name": getQueryName(), - "partial_name": getQueryPartialName(), - "project_id": getQueryProjectID(), - "skip": getQuerySkip(), - "tags": getQueryTags(), - "space_id": getQuerySpaceID(), - "tenants": { - Computed: true, - Description: "A list of tenants that match the filter(s).", - Elem: &schema.Resource{Schema: dataSchema}, - Optional: false, - Type: schema.TypeList, - }, - "take": getQueryTake(), - } -} - -func getTenantSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "cloned_from_tenant_id": { - Description: "The ID of the tenant from which this tenant was cloned.", - Optional: true, - Type: schema.TypeString, - }, - "description": getDescriptionSchema("tenant"), - "id": getIDSchema(), - "name": getNameSchema(true), - "space_id": getSpaceIDSchema(), - "tenant_tags": getTenantTagsSchema(), - } -} - -func setTenant(ctx context.Context, d *schema.ResourceData, tenant *tenants.Tenant) error { - d.Set("cloned_from_tenant_id", tenant.ClonedFromTenantID) - d.Set("description", tenant.Description) - d.Set("id", tenant.GetID()) - d.Set("name", tenant.Name) - d.Set("space_id", tenant.SpaceID) - - if err := d.Set("tenant_tags", tenant.TenantTags); err != nil { - return fmt.Errorf("error setting tenant_tags: %s", err) - } - - return nil -} diff --git a/octopusdeploy/schema_tenant_test.go b/octopusdeploy/schema_tenant_test.go deleted file mode 100644 index 0a1d00d18..000000000 --- a/octopusdeploy/schema_tenant_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package octopusdeploy - -import ( - "reflect" - "testing" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/stretchr/testify/require" -) - -func TestFlattenTenant(t *testing.T) { - clonedFromTenantID := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - description := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - id := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - projectEnvironments := map[string][]string{} - spaceID := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) - tenantTags := []string{acctest.RandStringFromCharSet(20, acctest.CharSetAlpha), acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)} - - expectedExpanded := tenants.NewTenant(name) - expectedExpanded.ClonedFromTenantID = clonedFromTenantID - expectedExpanded.Description = description - expectedExpanded.ID = id - expectedExpanded.ProjectEnvironments = projectEnvironments - expectedExpanded.SpaceID = spaceID - expectedExpanded.TenantTags = tenantTags - - expectedFlattened := map[string]interface{}{ - "cloned_from_tenant_id": clonedFromTenantID, - "description": description, - "id": id, - "name": name, - "space_id": spaceID, - "tenant_tags": tenantTags, - } - - actualFlattened := flattenTenant(expectedExpanded) - require.True(t, reflect.DeepEqual(expectedFlattened, actualFlattened)) -} diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index fc1128666..68686d521 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -95,6 +95,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func() NewProjectResource, NewDockerContainerRegistryFeedResource, NewRunbookResource, + NewTenantResource, } } diff --git a/octopusdeploy_framework/resource_tenant.go b/octopusdeploy_framework/resource_tenant.go new file mode 100644 index 000000000..3fcde55b3 --- /dev/null +++ b/octopusdeploy_framework/resource_tenant.go @@ -0,0 +1,174 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "sort" +) + +type tenantTypeResource struct { + *Config +} + +func NewTenantResource() resource.Resource { + return &tenantTypeResource{} +} + +func (r *tenantTypeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = util.GetTypeName("tenant") +} + +func (r *tenantTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: schemas.GetTenantResourceSchema(), + Description: "This resource manages tenants in Octopus Deploy.", + } +} + +func (r *tenantTypeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.Config = ResourceConfiguration(req, resp) +} + +func (r *tenantTypeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + internal.Mutex.Lock() + defer internal.Mutex.Unlock() + + var data *schemas.TenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + tenant, err := mapStateToTenant(data) + if err != nil { + return + } + + tflog.Info(ctx, fmt.Sprintf("creating Tenant: %s", tenant.Name)) + + createdTenant, err := tenants.Add(r.Config.Client, tenant) + if err != nil { + resp.Diagnostics.AddError("unable to create tenant", err.Error()) + return + } + + mapTenantToState(data, createdTenant) + + tflog.Info(ctx, fmt.Sprintf("Tenant created (%s)", data.ID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *tenantTypeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *schemas.TenantModel + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Info(ctx, fmt.Sprintf("reading Tenant (%s)", data.ID)) + + client := r.Config.Client + tenant, err := tenants.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) + if err != nil { + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "tenant"); err != nil { + resp.Diagnostics.AddError("unable to load tenant", err.Error()) + } + return + } + + mapTenantToState(data, tenant) + + tflog.Info(ctx, fmt.Sprintf("Tenant read (%s)", tenant.GetID())) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *tenantTypeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + internal.Mutex.Lock() + defer internal.Mutex.Unlock() + + var data, state *schemas.TenantModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("updating tenant '%s'", data.ID.ValueString())) + + tenantFromApi, err := tenants.GetByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()) + + tenant, err := mapStateToTenant(data) + tenant.ID = state.ID.ValueString() + if err != nil { + resp.Diagnostics.AddError("unable to map to tenant", err.Error()) + return + } + + tflog.Info(ctx, fmt.Sprintf("updating Tenant (%s)", data.ID)) + + tenant.ProjectEnvironments = tenantFromApi.ProjectEnvironments + updatedTenant, err := tenants.Update(r.Config.Client, tenant) + if err != nil { + resp.Diagnostics.AddError("unable to update tenant", err.Error()) + return + } + + mapTenantToState(data, updatedTenant) + + tflog.Info(ctx, fmt.Sprintf("Tenant updated (%s)", data.ID)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *tenantTypeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + internal.Mutex.Lock() + defer internal.Mutex.Unlock() + + var data schemas.TenantModel + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if err := tenants.DeleteByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("unable to delete tenant", err.Error()) + return + } +} + +func mapStateToTenant(data *schemas.TenantModel) (*tenants.Tenant, error) { + tenant := tenants.NewTenant(data.Name.ValueString()) + tenant.ID = data.ID.ValueString() + tenant.ClonedFromTenantID = data.ClonedFromTenantId.ValueString() + tenant.Description = data.Description.ValueString() + tenant.SpaceID = data.SpaceID.ValueString() + if len(data.TenantTags.Elements()) > 0 { + tenant.TenantTags = util.ExpandStringList(data.TenantTags) + } else { + tenant.TenantTags = []string{} + } + sort.Strings(tenant.TenantTags) + + return tenant, nil +} + +func mapTenantToState(data *schemas.TenantModel, tenant *tenants.Tenant) { + data.ID = types.StringValue(tenant.ID) + data.ClonedFromTenantId = types.StringValue(tenant.ClonedFromTenantID) + data.Description = types.StringValue(tenant.Description) + data.SpaceID = types.StringValue(tenant.SpaceID) + data.Name = types.StringValue(tenant.Name) + sort.Strings(tenant.TenantTags) + data.TenantTags = util.Ternary(tenant.TenantTags != nil && len(tenant.TenantTags) > 0, util.FlattenStringList(tenant.TenantTags), types.ListValueMust(types.StringType, make([]attr.Value, 0))) +} diff --git a/octopusdeploy_framework/resource_tenant_migration_test.go b/octopusdeploy_framework/resource_tenant_migration_test.go new file mode 100644 index 000000000..0ca7404e7 --- /dev/null +++ b/octopusdeploy_framework/resource_tenant_migration_test.go @@ -0,0 +1,109 @@ +package octopusdeploy_framework + +import ( + "fmt" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/stretchr/testify/assert" + "os" + "sort" + "testing" +) + +func TestTenantResource_UpgradeFromSDK_ToPluginFramework(t *testing.T) { + // override the path to check for terraformrc file and test against the real 0.21.1 version + os.Setenv("TF_CLI_CONFIG_FILE=", "") + + name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + resource.Test(t, resource.TestCase{ + CheckDestroy: testTenantProjectDestroyed, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "octopusdeploy": { + VersionConstraint: "0.22.0", + Source: "OctopusDeployLabs/octopusdeploy", + }, + }, + Config: tenantConfig(), + }, + { + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Config: tenantConfig(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + { + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Config: updatedTenantResourceConfig(), + Check: resource.ComposeTestCheckFunc( + testTenantResourceUpdated(t, name), + ), + }, + }, + }) +} + +func tenantConfig() string { + return fmt.Sprintf(` + resource "octopusdeploy_tenant" "tenant1" { + name = "tenant test" + }`) +} + +func updatedTenantResourceConfig() string { + return fmt.Sprintf(` +resource "octopusdeploy_tag_set" "tagset_tag1" { + name = "tag1" + description = "Test tagset" + sort_order = 0 +} + +resource "octopusdeploy_tag" "tag_a" { + name = "a" + color = "#333333" + description = "tag a" + sort_order = 2 + tag_set_id = octopusdeploy_tag_set.tagset_tag1.id +} + +resource "octopusdeploy_tag" "tag_b" { + name = "b" + color = "#333333" + description = "tag b" + sort_order = 3 + tag_set_id = octopusdeploy_tag_set.tagset_tag1.id +} + +resource "octopusdeploy_tenant" "tenant1" { + name = "Updated tenant" + description = "Updated description" + tenant_tags = ["tag1/a", "tag1/b"] + depends_on = [octopusdeploy_tag.tag_a, octopusdeploy_tag.tag_b] +}`) +} + +func testTenantResourceUpdated(t *testing.T, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + tenantId := s.RootModule().Resources["octopusdeploy_tenant.tenant1"].Primary.ID + tenant, err := octoClient.Tenants.GetByID(tenantId) + if err != nil { + return fmt.Errorf("failed to retrieve tenant by ID: %s", err) + } + sort.Strings(tenant.TenantTags) + + assert.NotEmpty(t, "Tenant ID did not match expected value", tenant.ID) + assert.Equal(t, fmt.Sprintf("Updated description"), tenant.Description) + assert.Equal(t, "", tenant.ClonedFromTenantID) + assert.Equal(t, "Updated tenant", tenant.Name) + assert.Equal(t, "Spaces-1", tenant.SpaceID) + assert.Equal(t, []string{"tag1/a", "tag1/b"}, tenant.TenantTags) + + return nil + } +} diff --git a/octopusdeploy/resource_tenant_test.go b/octopusdeploy_framework/resource_tenant_test.go similarity index 95% rename from octopusdeploy/resource_tenant_test.go rename to octopusdeploy_framework/resource_tenant_test.go index 22b03b365..cd5a28b72 100644 --- a/octopusdeploy/resource_tenant_test.go +++ b/octopusdeploy_framework/resource_tenant_test.go @@ -1,17 +1,16 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "path/filepath" "testing" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/octoclient" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/test" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccTenantBasic(t *testing.T) { @@ -34,7 +33,7 @@ func TestAccTenantBasic(t *testing.T) { resource.Test(t, resource.TestCase{ CheckDestroy: testAccTenantCheckDestroy, - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: ProtoV6ProviderFactories(), Steps: []resource.TestStep{ { diff --git a/octopusdeploy_framework/schemas/tenant.go b/octopusdeploy_framework/schemas/tenant.go index 41d55b7d1..497540621 100644 --- a/octopusdeploy_framework/schemas/tenant.go +++ b/octopusdeploy_framework/schemas/tenant.go @@ -5,16 +5,21 @@ import ( "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" "github.com/hashicorp/terraform-plugin-framework/attr" datasourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/types" ) type TenantModel struct { ClonedFromTenantId types.String `tfsdk:"cloned_from_tenant_id"` Description types.String `tfsdk:"description"` - ID types.String `tfsdk:"id"` Name types.String `tfsdk:"name"` SpaceID types.String `tfsdk:"space_id"` TenantTags types.List `tfsdk:"tenant_tags"` + + ResourceModel } type TenantsModel struct { @@ -105,3 +110,27 @@ func GetTenantDataSourceSchema() map[string]datasourceSchema.Attribute { }, } } + +func GetTenantResourceSchema() map[string]resourceSchema.Attribute { + return map[string]resourceSchema.Attribute{ + "cloned_from_tenant_id": resourceSchema.StringAttribute{ + Description: "The ID of the tenant from which this tenant was cloned.", + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + }, + "description": util.GetDescriptionResourceSchema("tenant"), + "id": util.GetIdResourceSchema(), + "name": util.GetNameResourceSchema(true), + "space_id": util.GetSpaceIdResourceSchema("tenant"), + "tenant_tags": resourceSchema.ListAttribute{ + Description: "A list of tenant tags associated with this resource.", + ElementType: types.StringType, + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + }, + } +} From d10c46797e4914498e46dff5ab571661a83cf8dd Mon Sep 17 00:00:00 2001 From: Huy Nguyen <162080607+HuyPhanNguyen@users.noreply.github.com> Date: Mon, 12 Aug 2024 15:00:47 +1000 Subject: [PATCH 11/12] chore!: Migrate username_password_account resource (#721) * Add username_password_account resource * Fix test fail * Add import and import test * Update octopusdeploy_framework/schemas/username_password_account.go Co-authored-by: Henrik Andersson * Update follow new way to handle a missing resource in the Read function * fix the document * fix issue list set to state as null * tidy --------- Co-authored-by: Henrik Andersson --- docs/resources/username_password_account.md | 2 +- octopusdeploy/provider.go | 1 - ...bernetes_cluster_deployment_target_test.go | 7 + .../resource_username_password_account.go | 95 -------- .../schema_username_password_account.go | 90 -------- octopusdeploy_framework/framework_provider.go | 1 + .../resource_username_password_account.go | 210 ++++++++++++++++++ ...rname_password_account_integration_test.go | 2 +- ...resource_username_password_account_test.go | 87 ++++++-- .../schemas/username_password_account.go | 40 ++++ 10 files changed, 333 insertions(+), 202 deletions(-) delete mode 100644 octopusdeploy/resource_username_password_account.go delete mode 100644 octopusdeploy/schema_username_password_account.go create mode 100644 octopusdeploy_framework/resource_username_password_account.go rename {octopusdeploy => octopusdeploy_framework}/resource_username_password_account_integration_test.go (98%) rename {octopusdeploy => octopusdeploy_framework}/resource_username_password_account_test.go (56%) create mode 100644 octopusdeploy_framework/schemas/username_password_account.go diff --git a/docs/resources/username_password_account.md b/docs/resources/username_password_account.md index cd949fa53..f00bd5ad6 100644 --- a/docs/resources/username_password_account.md +++ b/docs/resources/username_password_account.md @@ -23,7 +23,7 @@ resource "octopusdeploy_username_password_account" "example" { ### Required -- `name` (String) The name of this resource. +- `name` (String) The name of the username-password account. - `username` (String, Sensitive) The username associated with this resource. ### Optional diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index ebdaf0b94..ed362eb12 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -71,7 +71,6 @@ func Provider() *schema.Provider { "octopusdeploy_token_account": resourceTokenAccount(), "octopusdeploy_user": resourceUser(), "octopusdeploy_user_role": resourceUserRole(), - "octopusdeploy_username_password_account": resourceUsernamePasswordAccount(), }, Schema: map[string]*schema.Schema{ "address": { diff --git a/octopusdeploy/resource_kubernetes_cluster_deployment_target_test.go b/octopusdeploy/resource_kubernetes_cluster_deployment_target_test.go index 0b92199bd..e1973adc0 100644 --- a/octopusdeploy/resource_kubernetes_cluster_deployment_target_test.go +++ b/octopusdeploy/resource_kubernetes_cluster_deployment_target_test.go @@ -149,6 +149,13 @@ func testAccKubernetesClusterDeploymentTargetBasic(accountLocalName string, acco }`, localName, clusterURL, environmentID, name, userRoleID, usernamePasswordAccountID) } +func testUsernamePasswordMinimum(localName string, name string, username string) string { + return fmt.Sprintf(`resource "octopusdeploy_username_password_account" "%s" { + name = "%s" + username = "%s" + }`, localName, name, username) +} + func testAccKubernetesClusterDeploymentTargetGcp( accountLocalName string, accountName string, diff --git a/octopusdeploy/resource_username_password_account.go b/octopusdeploy/resource_username_password_account.go deleted file mode 100644 index 4d6ba8b79..000000000 --- a/octopusdeploy/resource_username_password_account.go +++ /dev/null @@ -1,95 +0,0 @@ -package octopusdeploy - -import ( - "context" - "log" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceUsernamePasswordAccount() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceUsernamePasswordAccountCreate, - DeleteContext: resourceUsernamePasswordAccountDelete, - Description: "This resource manages username-password accounts in Octopus Deploy.", - Importer: getImporter(), - ReadContext: resourceUsernamePasswordAccountRead, - Schema: getUsernamePasswordAccountSchema(), - UpdateContext: resourceUsernamePasswordAccountUpdate, - } -} - -func resourceUsernamePasswordAccountCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - account := expandUsernamePasswordAccount(d) - - log.Printf("[INFO] creating username-password account: %#v", account) - - client := m.(*client.Client) - createdAccount, err := accounts.Add(client, account) - if err != nil { - return diag.FromErr(err) - } - - if err := setUsernamePasswordAccount(ctx, d, createdAccount.(*accounts.UsernamePasswordAccount)); err != nil { - return diag.FromErr(err) - } - - d.SetId(createdAccount.GetID()) - - log.Printf("[INFO] username-password account created (%s)", d.Id()) - return nil -} - -func resourceUsernamePasswordAccountDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] deleting username-password account (%s)", d.Id()) - - client := m.(*client.Client) - if err := accounts.DeleteByID(client, d.Get("space_id").(string), d.Id()); err != nil { - return diag.FromErr(err) - } - - d.SetId("") - - log.Printf("[INFO] username-password account deleted") - return nil -} - -func resourceUsernamePasswordAccountRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] reading username-password account (%s)", d.Id()) - - client := m.(*client.Client) - accountResource, err := accounts.GetByID(client, d.Get("space_id").(string), d.Id()) - if err != nil { - return errors.ProcessApiError(ctx, d, err, "username-password account") - } - - if err := setUsernamePasswordAccount(ctx, d, accountResource.(*accounts.UsernamePasswordAccount)); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] username-password account read: (%s)", d.Id()) - return nil -} - -func resourceUsernamePasswordAccountUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - account := expandUsernamePasswordAccount(d) - - log.Printf("[INFO] updating username-password account: %#v", account) - - client := m.(*client.Client) - updatedAccount, err := accounts.Update(client, account) - if err != nil { - return diag.FromErr(err) - } - - if err := setUsernamePasswordAccount(ctx, d, updatedAccount.(*accounts.UsernamePasswordAccount)); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] username-password account updated (%s)", d.Id()) - return nil -} diff --git a/octopusdeploy/schema_username_password_account.go b/octopusdeploy/schema_username_password_account.go deleted file mode 100644 index 4e0c50b65..000000000 --- a/octopusdeploy/schema_username_password_account.go +++ /dev/null @@ -1,90 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func expandUsernamePasswordAccount(d *schema.ResourceData) accounts.IUsernamePasswordAccount { - name := d.Get("name").(string) - - account, _ := accounts.NewUsernamePasswordAccount(name) - account.SetID(d.Id()) - account.SetPassword(core.NewSensitiveValue(d.Get("password").(string))) - - if v, ok := d.GetOk("description"); ok { - account.SetDescription(v.(string)) - } - - if v, ok := d.GetOk("environments"); ok { - account.SetEnvironmentIDs(getSliceFromTerraformTypeList(v)) - } - - if v, ok := d.GetOk("space_id"); ok { - account.SetSpaceID(v.(string)) - } - - if v, ok := d.GetOk("tenanted_deployment_participation"); ok { - account.SetTenantedDeploymentMode(core.TenantedDeploymentMode(v.(string))) - } - - if v, ok := d.GetOk("tenants"); ok { - account.SetTenantIDs(getSliceFromTerraformTypeList(v)) - } - - if v, ok := d.GetOk("tenant_tags"); ok { - account.SetTenantTags(getSliceFromTerraformTypeList(v)) - } - - if v, ok := d.GetOk("username"); ok { - account.SetUsername(v.(string)) - } - - return account -} - -func setUsernamePasswordAccount(ctx context.Context, d *schema.ResourceData, account *accounts.UsernamePasswordAccount) error { - d.Set("description", account.GetDescription()) - - if err := d.Set("environments", account.GetEnvironmentIDs()); err != nil { - return fmt.Errorf("error setting environments: %s", err) - } - - d.Set("id", account.GetID()) - d.Set("name", account.GetName()) - d.Set("space_id", account.GetSpaceID()) - d.Set("tenanted_deployment_participation", account.GetTenantedDeploymentMode()) - - if err := d.Set("tenants", account.GetTenantIDs()); err != nil { - return fmt.Errorf("error setting tenants: %s", err) - } - - if err := d.Set("tenant_tags", account.TenantTags); err != nil { - return fmt.Errorf("error setting tenant_tags: %s", err) - } - - d.Set("username", account.Username) - - d.SetId(account.GetID()) - - return nil -} - -func getUsernamePasswordAccountSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "description": getDescriptionSchema("username/password account"), - "environments": getEnvironmentsSchema(), - "id": getIDSchema(), - "name": getNameSchema(true), - "password": getPasswordSchema(false), - "space_id": getSpaceIDSchema(), - "tenanted_deployment_participation": getTenantedDeploymentSchema(), - "tenants": getTenantsSchema(), - "tenant_tags": getTenantTagsSchema(), - "username": getUsernameSchema(true), - } -} diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index 68686d521..0e1ac1453 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -94,6 +94,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func() NewVariableResource, NewProjectResource, NewDockerContainerRegistryFeedResource, + NewUsernamePasswordAccountResource, NewRunbookResource, NewTenantResource, } diff --git a/octopusdeploy_framework/resource_username_password_account.go b/octopusdeploy_framework/resource_username_password_account.go new file mode 100644 index 000000000..307ffe3fd --- /dev/null +++ b/octopusdeploy_framework/resource_username_password_account.go @@ -0,0 +1,210 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +var _ resource.Resource = &usernamePasswordAccountResource{} +var _ resource.ResourceWithImportState = &usernamePasswordAccountResource{} + +type usernamePasswordAccountResource struct { + *Config +} + +func NewUsernamePasswordAccountResource() resource.Resource { + return &usernamePasswordAccountResource{} +} + +func (r *usernamePasswordAccountResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = util.GetTypeName("username_password_account") +} + +func (r *usernamePasswordAccountResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schemas.GetUsernamePasswordAccountResourceSchema() +} + +func (r *usernamePasswordAccountResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.Config = ResourceConfiguration(req, resp) +} +func (r *usernamePasswordAccountResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan schemas.UsernamePasswordAccountResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Creating username password account", map[string]interface{}{ + "name": plan.Name.ValueString(), + }) + + account := expandUsernamePasswordAccount(ctx, plan) + createdAccount, err := accounts.Add(r.Client, account) + if err != nil { + resp.Diagnostics.AddError("Error creating username password account", err.Error()) + return + } + + state := flattenUsernamePasswordAccount(ctx, createdAccount.(*accounts.UsernamePasswordAccount), plan) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *usernamePasswordAccountResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state schemas.UsernamePasswordAccountResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + account, err := accounts.GetByID(r.Client, state.SpaceID.ValueString(), state.ID.ValueString()) + if err != nil { + if err := errors.ProcessApiErrorV2(ctx, resp, state, err, "usernamePasswordAccountResource"); err != nil { + resp.Diagnostics.AddError("unable to load username password account", err.Error()) + } + return + } + + newState := flattenUsernamePasswordAccount(ctx, account.(*accounts.UsernamePasswordAccount), state) + resp.Diagnostics.Append(resp.State.Set(ctx, newState)...) +} + +func (r *usernamePasswordAccountResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan schemas.UsernamePasswordAccountResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + account := expandUsernamePasswordAccount(ctx, plan) + updatedAccount, err := accounts.Update(r.Client, account) + if err != nil { + resp.Diagnostics.AddError("Error updating username password account", err.Error()) + return + } + + state := flattenUsernamePasswordAccount(ctx, updatedAccount.(*accounts.UsernamePasswordAccount), plan) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *usernamePasswordAccountResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state schemas.UsernamePasswordAccountResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + err := accounts.DeleteByID(r.Client, state.SpaceID.ValueString(), state.ID.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Error deleting username password account", err.Error()) + return + } +} + +func (r *usernamePasswordAccountResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + accountID := req.ID + + account, err := accounts.GetByID(r.Client, r.Client.GetSpaceID(), accountID) + if err != nil { + resp.Diagnostics.AddError( + "Error reading username password account", + fmt.Sprintf("Unable to read username password account with ID %s: %s", accountID, err.Error()), + ) + return + } + + usernamePasswordAccount, ok := account.(*accounts.UsernamePasswordAccount) + if !ok { + resp.Diagnostics.AddError( + "Unexpected account type", + fmt.Sprintf("Expected username password account, got: %T", account), + ) + return + } + + state := schemas.UsernamePasswordAccountResourceModel{ + SpaceID: types.StringValue(usernamePasswordAccount.GetSpaceID()), + Name: types.StringValue(usernamePasswordAccount.GetName()), + Description: types.StringValue(usernamePasswordAccount.GetDescription()), + Username: types.StringValue(usernamePasswordAccount.GetUsername()), + TenantedDeploymentParticipation: types.StringValue(string(usernamePasswordAccount.GetTenantedDeploymentMode())), + Environments: flattenStringList(usernamePasswordAccount.GetEnvironmentIDs(), types.ListNull(types.StringType)), + Tenants: flattenStringList(usernamePasswordAccount.GetTenantIDs(), types.ListNull(types.StringType)), + TenantTags: flattenStringList(usernamePasswordAccount.TenantTags, types.ListNull(types.StringType)), + Password: types.StringNull(), + } + state.ID = types.StringValue(usernamePasswordAccount.ID) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func expandUsernamePasswordAccount(ctx context.Context, model schemas.UsernamePasswordAccountResourceModel) *accounts.UsernamePasswordAccount { + account, _ := accounts.NewUsernamePasswordAccount(model.Name.ValueString()) + + account.SetID(model.ID.ValueString()) + account.SetDescription(model.Description.ValueString()) + account.SetSpaceID(model.SpaceID.ValueString()) + account.SetUsername(model.Username.ValueString()) + account.SetPassword(core.NewSensitiveValue(model.Password.ValueString())) + account.SetEnvironmentIDs(expandStringList(model.Environments)) + account.SetTenantedDeploymentMode(core.TenantedDeploymentMode(model.TenantedDeploymentParticipation.ValueString())) + account.SetTenantIDs(expandStringList(model.Tenants)) + account.SetTenantTags(expandStringList(model.TenantTags)) + + return account +} + +func flattenUsernamePasswordAccount(ctx context.Context, account *accounts.UsernamePasswordAccount, model schemas.UsernamePasswordAccountResourceModel) schemas.UsernamePasswordAccountResourceModel { + model.ID = types.StringValue(account.GetID()) + model.SpaceID = types.StringValue(account.GetSpaceID()) + model.Name = types.StringValue(account.GetName()) + model.Description = types.StringValue(account.GetDescription()) + model.Username = types.StringValue(account.GetUsername()) + model.TenantedDeploymentParticipation = types.StringValue(string(account.GetTenantedDeploymentMode())) + + model.Environments = flattenStringList(account.GetEnvironmentIDs(), model.Environments) + model.Tenants = flattenStringList(account.GetTenantIDs(), model.Tenants) + model.TenantTags = flattenStringList(account.TenantTags, model.TenantTags) + + // Note: We don't flatten the password as it's sensitive and not returned by the API + + return model +} + +func expandStringList(list types.List) []string { + if list.IsNull() || list.IsUnknown() { + return nil + } + + var result []string + list.ElementsAs(context.Background(), &result, false) + if len(result) == 0 { + return nil + } + + return result +} + +func flattenStringList(slice []string, currentList types.List) types.List { + if len(slice) == 0 && currentList.IsNull() { + return types.ListNull(types.StringType) + } + if slice == nil { + return types.ListNull(types.StringType) + } + + valueSlice := make([]attr.Value, len(slice)) + for i, s := range slice { + valueSlice[i] = types.StringValue(s) + } + + return types.ListValueMust(types.StringType, valueSlice) +} diff --git a/octopusdeploy/resource_username_password_account_integration_test.go b/octopusdeploy_framework/resource_username_password_account_integration_test.go similarity index 98% rename from octopusdeploy/resource_username_password_account_integration_test.go rename to octopusdeploy_framework/resource_username_password_account_integration_test.go index b5ea00af4..a730246f1 100644 --- a/octopusdeploy/resource_username_password_account_integration_test.go +++ b/octopusdeploy_framework/resource_username_password_account_integration_test.go @@ -1,4 +1,4 @@ -package octopusdeploy +package octopusdeploy_framework import ( "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/accounts" diff --git a/octopusdeploy/resource_username_password_account_test.go b/octopusdeploy_framework/resource_username_password_account_test.go similarity index 56% rename from octopusdeploy/resource_username_password_account_test.go rename to octopusdeploy_framework/resource_username_password_account_test.go index e1a29bd9f..25977a673 100644 --- a/octopusdeploy/resource_username_password_account_test.go +++ b/octopusdeploy_framework/resource_username_password_account_test.go @@ -1,16 +1,16 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/octoclient" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/test" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "testing" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccUsernamePasswordBasic(t *testing.T) { @@ -26,8 +26,7 @@ func TestAccUsernamePasswordBasic(t *testing.T) { config := testUsernamePasswordBasic(localName, description, name, username, password, tenantedDeploymentParticipation) resource.Test(t, resource.TestCase{ - CheckDestroy: testAccountCheckDestroy, - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: ProtoV6ProviderFactories(), Steps: []resource.TestStep{ { @@ -48,6 +47,17 @@ func TestAccUsernamePasswordBasic(t *testing.T) { }) } +func testAccountExists(prefix string) resource.TestCheckFunc { + return func(s *terraform.State) error { + accountID := s.RootModule().Resources[prefix].Primary.ID + if _, err := octoClient.Accounts.GetByID(accountID); err != nil { + return err + } + + return nil + } +} + func testUsernamePasswordBasic(localName string, description string, name string, username string, password string, tenantedDeploymentParticipation core.TenantedDeploymentMode) string { return fmt.Sprintf(`resource "octopusdeploy_username_password_account" "%s" { description = "%s" @@ -58,13 +68,6 @@ func testUsernamePasswordBasic(localName string, description string, name string }`, localName, description, name, password, tenantedDeploymentParticipation, username) } -func testUsernamePasswordMinimum(localName string, name string, username string) string { - return fmt.Sprintf(`resource "octopusdeploy_username_password_account" "%s" { - name = "%s" - username = "%s" - }`, localName, name, username) -} - // TestUsernamePasswordVariableResource verifies that a project variable referencing a username/password account // can be created func TestUsernamePasswordVariableResource(t *testing.T) { @@ -111,3 +114,59 @@ func TestUsernamePasswordVariableResource(t *testing.T) { t.Fatalf("The variable must have type of UsernamePasswordAccount.") } } + +func TestAccUsernamePasswordAccountImport(t *testing.T) { + localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + resourceName := "octopusdeploy_username_password_account." + localName + + description := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + name := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + password := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + tenantedDeploymentParticipation := core.TenantedDeploymentModeTenantedOrUntenanted + username := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { TestAccPreCheck(t) }, + ProtoV6ProviderFactories: ProtoV6ProviderFactories(), + Steps: []resource.TestStep{ + // Create and test the resource + { + Config: testAccUsernamePasswordAccountBasic(localName, description, name, username, password, tenantedDeploymentParticipation), + Check: resource.ComposeTestCheckFunc( + testAccountExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "username", username), + resource.TestCheckResourceAttr(resourceName, "tenanted_deployment_participation", string(tenantedDeploymentParticipation)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"password"}, + ImportStateIdFunc: testAccUsernamePasswordAccountImportStateIdFunc(resourceName), + }, + }, + }) +} + +func testAccUsernamePasswordAccountImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + + return rs.Primary.ID, nil + } +} +func testAccUsernamePasswordAccountBasic(localName, description, name, username, password string, tenantedDeploymentParticipation core.TenantedDeploymentMode) string { + return fmt.Sprintf(`resource "octopusdeploy_username_password_account" "%s" { + name = "%s" + description = "%s" + username = "%s" + password = "%s" + tenanted_deployment_participation = "%s" + }`, localName, name, description, username, password, tenantedDeploymentParticipation) +} diff --git a/octopusdeploy_framework/schemas/username_password_account.go b/octopusdeploy_framework/schemas/username_password_account.go new file mode 100644 index 000000000..c5bff07fc --- /dev/null +++ b/octopusdeploy_framework/schemas/username_password_account.go @@ -0,0 +1,40 @@ +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" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func GetUsernamePasswordAccountResourceSchema() schema.Schema { + return schema.Schema{ + Description: "This resource manages username-password accounts in Octopus Deploy.", + Attributes: map[string]schema.Attribute{ + "id": util.ResourceString().Optional().Computed().PlanModifiers(stringplanmodifier.UseStateForUnknown()).Description("The unique ID for this resource.").Build(), + "space_id": util.ResourceString().Optional().Computed().PlanModifiers(stringplanmodifier.UseStateForUnknown()).Description("The space ID associated with this resource.").Build(), + "name": util.ResourceString().Required().Description("The name of the username-password account.").Build(), + "description": util.ResourceString().Optional().Computed().PlanModifiers(stringplanmodifier.UseStateForUnknown()).Default("").Description("The description of this username/password account.").Build(), + "environments": util.ResourceList(types.StringType).Optional().Computed().Description("A list of environment IDs associated with this resource.").Build(), + "password": util.ResourceString().Optional().Sensitive().Description("The password associated with this resource.").Build(), + "tenanted_deployment_participation": util.ResourceString().Optional().Optional().Computed().PlanModifiers(stringplanmodifier.UseStateForUnknown()).Description("The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`.").Build(), + "tenants": util.ResourceList(types.StringType).Optional().Computed().Description("A list of tenant IDs associated with this resource.").Build(), + "tenant_tags": util.ResourceList(types.StringType).Optional().Computed().Description("A list of tenant tags associated with this resource.").Build(), + "username": util.ResourceString().Required().Sensitive().Description("The username associated with this resource.").Build(), + }, + } +} + +type UsernamePasswordAccountResourceModel struct { + SpaceID types.String `tfsdk:"space_id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Environments types.List `tfsdk:"environments"` + Password types.String `tfsdk:"password"` + TenantedDeploymentParticipation types.String `tfsdk:"tenanted_deployment_participation"` + Tenants types.List `tfsdk:"tenants"` + TenantTags types.List `tfsdk:"tenant_tags"` + Username types.String `tfsdk:"username"` + + ResourceModel +} From 44acc60e960ed19d524bcd26852fd3267b4ccf5d Mon Sep 17 00:00:00 2001 From: Isaac Calligeros <101079287+IsaacCalligeros95@users.noreply.github.com> Date: Mon, 12 Aug 2024 14:55:32 +0930 Subject: [PATCH 12/12] Chore!: Migrate script module resource (#712) --- docs/data-sources/script_modules | 26 +-- docs/data-sources/script_modules.md | 26 +-- docs/resources/script_module.md | 8 +- octopusdeploy/data_source_script_modules.go | 48 ---- octopusdeploy/provider.go | 2 - octopusdeploy/resource_script_module.go | 106 --------- octopusdeploy/schema_script_modules.go | 155 ------------- .../data_source_script_modules.go | 74 ++++++ .../data_source_script_modules_test.go | 11 +- octopusdeploy_framework/framework_provider.go | 2 + .../resource_script_module.go | 130 +++++++++++ .../resource_script_module_test.go | 11 +- .../schemas/script_modules.go | 213 ++++++++++++++++++ 13 files changed, 459 insertions(+), 353 deletions(-) delete mode 100644 octopusdeploy/data_source_script_modules.go delete mode 100644 octopusdeploy/resource_script_module.go delete mode 100644 octopusdeploy/schema_script_modules.go create mode 100644 octopusdeploy_framework/data_source_script_modules.go rename {octopusdeploy => octopusdeploy_framework}/data_source_script_modules_test.go (79%) create mode 100644 octopusdeploy_framework/resource_script_module.go rename {octopusdeploy => octopusdeploy_framework}/resource_script_module_test.go (95%) create mode 100644 octopusdeploy_framework/schemas/script_modules.go diff --git a/docs/data-sources/script_modules b/docs/data-sources/script_modules index 6a12509e7..848de86dc 100644 --- a/docs/data-sources/script_modules +++ b/docs/data-sources/script_modules @@ -26,32 +26,32 @@ data "octopusdeploy_script_modules" "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. +- `partial_name` (String) A filter to search by a partial name. - `skip` (Number) A filter to specify the number of items to skip in the response. -- `space_id` (String) A Space ID to filter by. Will revert what is specified on the provider if not set. +- `space_id` (String) The space ID associated with this script module. - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `script_modules` (List of Object) A list of script modules that match the filter(s). (see [below for nested schema](#nestedatt--script_modules)) +- `id` (String) The unique ID for this resource. +- `script_modules` (Attributes List) (see [below for nested schema](#nestedatt--script_modules)) ### Nested Schema for `script_modules` Read-Only: -- `description` (String) -- `id` (String) -- `name` (String) -- `script` (Set of Object) (see [below for nested schema](#nestedobjatt--script_modules--script)) -- `space_id` (String) -- `variable_set_id` (String) +- `description` (String) The description of this script module. +- `id` (String) The unique ID for this resource. +- `name` (String) The name of this resource. +- `script` (Attributes List) The script associated with this script module. (see [below for nested schema](#nestedatt--script_modules--script)) +- `space_id` (String) The space ID associated with this Script Module. +- `variable_set_id` (String) The variable set ID for this script module. - + ### Nested Schema for `script_modules.script` Read-Only: -- `body` (String) -- `syntax` (String) \ No newline at end of file +- `body` (String) The body of this script module. +- `syntax` (String) The syntax of the script. Valid types are `Bash`, `CSharp`, `FSharp`, `PowerShell`, or `Python`. \ No newline at end of file diff --git a/docs/data-sources/script_modules.md b/docs/data-sources/script_modules.md index 35d7e16a8..2149ce0ca 100644 --- a/docs/data-sources/script_modules.md +++ b/docs/data-sources/script_modules.md @@ -27,34 +27,34 @@ data "octopusdeploy_script_modules" "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. +- `partial_name` (String) A filter to search by a partial name. - `skip` (Number) A filter to specify the number of items to skip in the response. -- `space_id` (String) A Space ID to filter by. Will revert what is specified on the provider if not set. +- `space_id` (String) The space ID associated with this script module. - `take` (Number) A filter to specify the number of items to take (or return) in the response. ### Read-Only -- `id` (String) An auto-generated identifier that includes the timestamp when this data source was last modified. -- `script_modules` (List of Object) A list of script modules that match the filter(s). (see [below for nested schema](#nestedatt--script_modules)) +- `id` (String) The unique ID for this resource. +- `script_modules` (Attributes List) (see [below for nested schema](#nestedatt--script_modules)) ### Nested Schema for `script_modules` Read-Only: -- `description` (String) -- `id` (String) -- `name` (String) -- `script` (Set of Object) (see [below for nested schema](#nestedobjatt--script_modules--script)) -- `space_id` (String) -- `variable_set_id` (String) +- `description` (String) The description of this script module. +- `id` (String) The unique ID for this resource. +- `name` (String) The name of this resource. +- `script` (Attributes List) The script associated with this script module. (see [below for nested schema](#nestedatt--script_modules--script)) +- `space_id` (String) The space ID associated with this Script Module. +- `variable_set_id` (String) The variable set ID for this script module. - + ### Nested Schema for `script_modules.script` Read-Only: -- `body` (String) -- `syntax` (String) +- `body` (String) The body of this script module. +- `syntax` (String) The syntax of the script. Valid types are `Bash`, `CSharp`, `FSharp`, `PowerShell`, or `Python`. diff --git a/docs/resources/script_module.md b/docs/resources/script_module.md index 15d81fb84..bb484cf04 100644 --- a/docs/resources/script_module.md +++ b/docs/resources/script_module.md @@ -3,12 +3,12 @@ page_title: "octopusdeploy_script_module Resource - terraform-provider-octopusdeploy" subcategory: "" description: |- - This resource manages script modules in Octopus Deploy. + --- # octopusdeploy_script_module (Resource) -This resource manages script modules in Octopus Deploy. + ## Example Usage @@ -30,13 +30,13 @@ resource "octopusdeploy_script_module" "example" { ### Required - `name` (String) The name of this resource. -- `script` (Block Set, Min: 1, Max: 1) The script associated with this script module. (see [below for nested schema](#nestedblock--script)) ### Optional - `description` (String) The description of this script module. - `id` (String) The unique ID for this resource. -- `space_id` (String) The space ID associated with this resource. +- `script` (Block List) The script associated with this script module. (see [below for nested schema](#nestedblock--script)) +- `space_id` (String) The space ID associated with this Script Module. - `variable_set_id` (String) The variable set ID for this script module. diff --git a/octopusdeploy/data_source_script_modules.go b/octopusdeploy/data_source_script_modules.go deleted file mode 100644 index f782e1410..000000000 --- a/octopusdeploy/data_source_script_modules.go +++ /dev/null @@ -1,48 +0,0 @@ -package octopusdeploy - -import ( - "context" - "time" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/scriptmodules" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceScriptModules() *schema.Resource { - return &schema.Resource{ - Description: "Provides information about existing script modules.", - ReadContext: dataSourceScriptModulesRead, - Schema: getScriptModuleDataSchema(), - } -} - -func dataSourceScriptModulesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - query := variables.LibraryVariablesQuery{ - ContentType: "ScriptModule", - IDs: expandArray(d.Get("ids").([]interface{})), - PartialName: d.Get("partial_name").(string), - Skip: d.Get("skip").(int), - Take: d.Get("take").(int), - } - - spaceID := d.Get("space_id").(string) - - client := m.(*client.Client) - existingScriptModules, err := scriptmodules.Get(client, spaceID, query) - if err != nil { - return diag.FromErr(err) - } - - flattenedScriptModules := []interface{}{} - for _, scriptModule := range existingScriptModules.Items { - flattenedScriptModules = append(flattenedScriptModules, flattenScriptModule(scriptModule)) - } - - d.Set("script_modules", flattenedScriptModules) - d.SetId("Script Modules " + time.Now().UTC().String()) - - return nil -} diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index ed362eb12..1c8ff40e9 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -25,7 +25,6 @@ func Provider() *schema.Provider { "octopusdeploy_machine_policies": dataSourceMachinePolicies(), "octopusdeploy_offline_package_drop_deployment_targets": dataSourceOfflinePackageDropDeploymentTargets(), "octopusdeploy_polling_tentacle_deployment_targets": dataSourcePollingTentacleDeploymentTargets(), - "octopusdeploy_script_modules": dataSourceScriptModules(), "octopusdeploy_ssh_connection_deployment_targets": dataSourceSSHConnectionDeploymentTargets(), "octopusdeploy_tag_sets": dataSourceTagSets(), "octopusdeploy_teams": dataSourceTeams(), @@ -60,7 +59,6 @@ func Provider() *schema.Provider { "octopusdeploy_project_scheduled_trigger": resourceProjectScheduledTrigger(), "octopusdeploy_runbook_process": resourceRunbookProcess(), "octopusdeploy_scoped_user_role": resourceScopedUserRole(), - "octopusdeploy_script_module": resourceScriptModule(), "octopusdeploy_ssh_connection_deployment_target": resourceSSHConnectionDeploymentTarget(), "octopusdeploy_ssh_key_account": resourceSSHKeyAccount(), "octopusdeploy_static_worker_pool": resourceStaticWorkerPool(), diff --git a/octopusdeploy/resource_script_module.go b/octopusdeploy/resource_script_module.go deleted file mode 100644 index faf0f5232..000000000 --- a/octopusdeploy/resource_script_module.go +++ /dev/null @@ -1,106 +0,0 @@ -package octopusdeploy - -import ( - "context" - "log" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/scriptmodules" - "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func resourceScriptModule() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceScriptModuleCreate, - DeleteContext: resourceScriptModuleDelete, - Description: "This resource manages script modules in Octopus Deploy.", - Importer: getImporter(), - ReadContext: resourceScriptModuleRead, - Schema: getScriptModuleSchema(), - UpdateContext: resourceScriptModuleUpdate, - } -} - -func resourceScriptModuleCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - scriptModule := expandScriptModule(d) - - log.Printf("[INFO] creating script module: %#v", scriptModule) - - client := m.(*client.Client) - createdScriptModule, err := scriptmodules.Add(client, scriptModule) - if err != nil { - return diag.FromErr(err) - } - - if err := setScriptModule(ctx, d, createdScriptModule); err != nil { - return diag.FromErr(err) - } - - d.SetId(createdScriptModule.GetID()) - - log.Printf("[INFO] script module created (%s)", d.Id()) - return nil -} - -func resourceScriptModuleDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] deleting script module (%s)", d.Id()) - - var spaceID string - if v, ok := d.GetOk("space_id"); ok { - spaceID = v.(string) - } - - client := m.(*client.Client) - err := scriptmodules.DeleteByID(client, spaceID, d.Id()) - if err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] script module deleted (%s)", d.Id()) - d.SetId("") - return nil -} - -func resourceScriptModuleRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] reading script module (%s)", d.Id()) - - var spaceID string - if v, ok := d.GetOk("space_id"); ok { - spaceID = v.(string) - } - - client := m.(*client.Client) - - scriptModule, err := scriptmodules.GetByID(client, spaceID, d.Id()) - if err != nil { - return errors.ProcessApiError(ctx, d, err, "script module") - } - - if err := setScriptModule(ctx, d, scriptModule); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] script module read (%s)", d.Id()) - return nil -} - -func resourceScriptModuleUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - log.Printf("[INFO] updating script module (%s)", d.Id()) - - scriptModule := expandScriptModule(d) - - client := m.(*client.Client) - updatedScriptModule, err := scriptmodules.Update(client, scriptModule) - if err != nil { - return diag.FromErr(err) - } - - if err := setScriptModule(ctx, d, updatedScriptModule); err != nil { - return diag.FromErr(err) - } - - log.Printf("[INFO] script module updated (%s)", d.Id()) - return nil -} diff --git a/octopusdeploy/schema_script_modules.go b/octopusdeploy/schema_script_modules.go deleted file mode 100644 index 906d2ea54..000000000 --- a/octopusdeploy/schema_script_modules.go +++ /dev/null @@ -1,155 +0,0 @@ -package octopusdeploy - -import ( - "context" - "fmt" - - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func expandScriptModule(d *schema.ResourceData) *variables.ScriptModule { - name := d.Get("name").(string) - - scriptModule := variables.NewScriptModule(name) - scriptModule.ID = d.Id() - - if v, ok := d.GetOk("description"); ok { - scriptModule.Description = v.(string) - } - - if v, ok := d.GetOk("script"); ok { - scripts := v.(*schema.Set).List() - for _, script := range scripts { - rawScript := script.(map[string]interface{}) - - if rawScript["body"] != nil { - scriptModule.ScriptBody = rawScript["body"].(string) - } - - if rawScript["syntax"] != nil { - scriptModule.Syntax = rawScript["syntax"].(string) - } - } - } - - if v, ok := d.GetOk("space_id"); ok { - scriptModule.SpaceID = v.(string) - } - - if v, ok := d.GetOk("variable_set_id"); ok { - scriptModule.VariableSetID = v.(string) - } - - return scriptModule -} - -func flattenScript(scriptModule *variables.ScriptModule) []interface{} { - if scriptModule == nil { - return nil - } - - flattenedScriptModules := make([]interface{}, 1) - flattenedScriptModules[0] = map[string]interface{}{ - "body": scriptModule.ScriptBody, - "syntax": scriptModule.Syntax, - } - - return flattenedScriptModules -} - -func flattenScriptModule(scriptModule *variables.ScriptModule) map[string]interface{} { - if scriptModule == nil { - return nil - } - - return map[string]interface{}{ - "description": scriptModule.Description, - "id": scriptModule.GetID(), - "name": scriptModule.Name, - "script": flattenScript(scriptModule), - "space_id": scriptModule.SpaceID, - "variable_set_id": scriptModule.VariableSetID, - } -} - -func getScriptModuleDataSchema() map[string]*schema.Schema { - dataSchema := getScriptModuleSchema() - setDataSchema(&dataSchema) - - return map[string]*schema.Schema{ - "id": getDataSchemaID(), - "space_id": getQuerySpaceID(), - "ids": getQueryIDs(), - "script_modules": { - Computed: true, - Description: "A list of script modules that match the filter(s).", - Elem: &schema.Resource{Schema: dataSchema}, - Optional: false, - Type: schema.TypeList, - }, - "partial_name": getQueryPartialName(), - "skip": getQuerySkip(), - "take": getQueryTake(), - } -} - -func getScriptModuleSchema() map[string]*schema.Schema { - return map[string]*schema.Schema{ - "description": getDescriptionSchema("script module"), - "id": getIDSchema(), - "name": getNameSchema(true), - "script": { - Description: "The script associated with this script module.", - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "body": { - Description: "The body of this script module.", - Required: true, - Type: schema.TypeString, - }, - "syntax": { - Description: "The syntax of the script. Valid types are `Bash`, `CSharp`, `FSharp`, `PowerShell`, or `Python`.", - Required: true, - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{ - "Bash", - "CSharp", - "FSharp", - "PowerShell", - "Python", - }, false)), - }, - }, - }, - MaxItems: 1, - MinItems: 1, - Type: schema.TypeSet, - }, - "space_id": getSpaceIDSchema(), - "variable_set_id": { - Computed: true, - Description: "The variable set ID for this script module.", - Optional: true, - Type: schema.TypeString, - }, - } -} - -func setScriptModule(ctx context.Context, d *schema.ResourceData, scriptModule *variables.ScriptModule) error { - d.Set("description", scriptModule.Description) - d.Set("name", scriptModule.Name) - - if err := d.Set("script", flattenScript(scriptModule)); err != nil { - return fmt.Errorf("error setting script: %s", err) - } - - d.Set("space_id", scriptModule.SpaceID) - d.Set("variable_set_id", scriptModule.VariableSetID) - - d.SetId(scriptModule.GetID()) - - return nil -} diff --git a/octopusdeploy_framework/data_source_script_modules.go b/octopusdeploy_framework/data_source_script_modules.go new file mode 100644 index 000000000..8374402de --- /dev/null +++ b/octopusdeploy_framework/data_source_script_modules.go @@ -0,0 +1,74 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/scriptmodules" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" + "github.com/hashicorp/terraform-plugin-framework/attr" + "time" + + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +type scriptModulesDataSource struct { + *Config +} + +func NewScriptModuleDataSource() datasource.DataSource { + return &scriptModulesDataSource{} +} + +func (l *scriptModulesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + tflog.Debug(ctx, "script modules datasource Metadata") + resp.TypeName = util.GetTypeName("script_modules") +} + +func (l *scriptModulesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + tflog.Debug(ctx, "script modules datasource Schema") + resp.Schema = schemas.GetDatasourceScriptModuleSchema() +} + +func (l *scriptModulesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + tflog.Debug(ctx, "script modules datasource Configure") + l.Config = DataSourceConfiguration(req, resp) +} + +func (l *scriptModulesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + tflog.Debug(ctx, "script modules datasource Read") + var data schemas.ScriptModuleDataSourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + query := variables.LibraryVariablesQuery{ + ContentType: "ScriptModule", + IDs: util.ExpandStringList(data.IDs), + PartialName: data.PartialName.ValueString(), + Skip: int(data.Skip.ValueInt64()), + Take: int(data.Take.ValueInt64()), + } + + spaceID := data.SpaceID.ValueString() + existingScriptModules, err := scriptmodules.Get(l.Config.Client, spaceID, query) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read script modules, got error: %s", err)) + return + } + + flattenedScriptModules := []attr.Value{} + for _, scriptModule := range existingScriptModules.Items { + flattenedScriptModules = append(flattenedScriptModules, schemas.FlattenScriptModule(scriptModule)) + } + + data.ScriptModules = types.ListValueMust(types.ObjectType{AttrTypes: schemas.ScriptModuleObjectType()}, + flattenedScriptModules) + data.ID = types.StringValue("Script Modules " + time.Now().UTC().String()) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/octopusdeploy/data_source_script_modules_test.go b/octopusdeploy_framework/data_source_script_modules_test.go similarity index 79% rename from octopusdeploy/data_source_script_modules_test.go rename to octopusdeploy_framework/data_source_script_modules_test.go index f5fa0450e..286a8ec5b 100644 --- a/octopusdeploy/data_source_script_modules_test.go +++ b/octopusdeploy_framework/data_source_script_modules_test.go @@ -1,12 +1,11 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccDataSourceScriptModules(t *testing.T) { @@ -15,7 +14,7 @@ func TestAccDataSourceScriptModules(t *testing.T) { take := 10 resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: ProtoV6ProviderFactories(), Steps: []resource.TestStep{ { diff --git a/octopusdeploy_framework/framework_provider.go b/octopusdeploy_framework/framework_provider.go index 0e1ac1453..894f15e82 100644 --- a/octopusdeploy_framework/framework_provider.go +++ b/octopusdeploy_framework/framework_provider.go @@ -71,6 +71,7 @@ func (p *octopusDeployFrameworkProvider) DataSources(ctx context.Context) []func NewVariablesDataSource, NewProjectsDataSource, NewTenantsDataSource, + NewScriptModuleDataSource, } } @@ -97,6 +98,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func() NewUsernamePasswordAccountResource, NewRunbookResource, NewTenantResource, + NewScriptModuleResource, } } diff --git a/octopusdeploy_framework/resource_script_module.go b/octopusdeploy_framework/resource_script_module.go new file mode 100644 index 000000000..8e0a1294f --- /dev/null +++ b/octopusdeploy_framework/resource_script_module.go @@ -0,0 +1,130 @@ +package octopusdeploy_framework + +import ( + "context" + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/scriptmodules" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas" + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +type scriptModuleTypeResource struct { + *Config +} + +func NewScriptModuleResource() resource.Resource { + return &scriptModuleTypeResource{} +} + +func (r *scriptModuleTypeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = util.GetTypeName("script_module") +} + +func (r *scriptModuleTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: schemas.GetScriptModuleResourceSchema(), + Blocks: schemas.GetScriptModuleSchemaBlock(), + } +} + +func (r *scriptModuleTypeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + r.Config = ResourceConfiguration(req, resp) +} + +func (r *scriptModuleTypeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + internal.Mutex.Lock() + defer internal.Mutex.Unlock() + + var data *schemas.ScriptModuleResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + scriptModule := schemas.MapFromScriptModuleToState(data) + + tflog.Info(ctx, fmt.Sprintf("creating Script Module: %s", scriptModule.Name)) + + createdScriptModule, err := scriptmodules.Add(r.Config.Client, scriptModule) + if err != nil { + resp.Diagnostics.AddError("unable to create script module", err.Error()) + return + } + + schemas.MapToScriptModuleFromState(data, createdScriptModule) + + tflog.Info(ctx, fmt.Sprintf("Script Module created (%s)", data.ID)) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *scriptModuleTypeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data *schemas.ScriptModuleResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Info(ctx, fmt.Sprintf("reading Script Module (%s)", data.ID)) + + client := r.Config.Client + scriptModule, err := scriptmodules.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString()) + if err != nil { + if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "Script Module"); err != nil { + resp.Diagnostics.AddError("unable to load script module", err.Error()) + } + return + } + + schemas.MapToScriptModuleFromState(data, scriptModule) + + tflog.Info(ctx, fmt.Sprintf("Script Module read (%s)", scriptModule.GetID())) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *scriptModuleTypeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data, state *schemas.ScriptModuleResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("updating script module '%s'", data.ID.ValueString())) + + scriptModule := schemas.MapFromScriptModuleToState(data) + scriptModule.ID = state.ID.ValueString() + + updatedScriptModule, err := scriptmodules.Update(r.Config.Client, scriptModule) + if err != nil { + resp.Diagnostics.AddError("unable to update script module", err.Error()) + return + } + + schemas.MapToScriptModuleFromState(data, updatedScriptModule) + + tflog.Info(ctx, fmt.Sprintf("Script Module updated (%s)", data.ID)) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *scriptModuleTypeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + internal.Mutex.Lock() + defer internal.Mutex.Unlock() + + var data schemas.ScriptModuleResourceModel + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if err := scriptmodules.DeleteByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("unable to delete script module", err.Error()) + return + } +} diff --git a/octopusdeploy/resource_script_module_test.go b/octopusdeploy_framework/resource_script_module_test.go similarity index 95% rename from octopusdeploy/resource_script_module_test.go rename to octopusdeploy_framework/resource_script_module_test.go index 3893ad972..c7828d6a3 100644 --- a/octopusdeploy/resource_script_module_test.go +++ b/octopusdeploy_framework/resource_script_module_test.go @@ -1,16 +1,15 @@ -package octopusdeploy +package octopusdeploy_framework import ( "fmt" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/octoclient" "github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework/test" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "path/filepath" "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccOctopusDeployScriptModuleBasic(t *testing.T) { @@ -24,7 +23,7 @@ func TestAccOctopusDeployScriptModuleBasic(t *testing.T) { resource.Test(t, resource.TestCase{ CheckDestroy: testScriptModuleCheckDestroy, - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: ProtoV6ProviderFactories(), Steps: []resource.TestStep{ { diff --git a/octopusdeploy_framework/schemas/script_modules.go b/octopusdeploy_framework/schemas/script_modules.go new file mode 100644 index 000000000..f74ab893b --- /dev/null +++ b/octopusdeploy_framework/schemas/script_modules.go @@ -0,0 +1,213 @@ +package schemas + +import ( + "github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + 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" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/variables" +) + +type ScriptModuleResourceModel struct { + Description types.String `tfsdk:"description"` + Name types.String `tfsdk:"name"` + SpaceID types.String `tfsdk:"space_id"` + VariableSetId types.String `tfsdk:"variable_set_id"` + Script types.List `tfsdk:"script"` + + ResourceModel +} + +type ScriptModuleDataSourceModel struct { + ID types.String `tfsdk:"id"` + SpaceID types.String `tfsdk:"space_id"` + IDs types.List `tfsdk:"ids"` + PartialName types.String `tfsdk:"partial_name"` + Skip types.Int64 `tfsdk:"skip"` + Take types.Int64 `tfsdk:"take"` + ScriptModules types.List `tfsdk:"script_modules"` +} + +func GetDatasourceScriptModuleSchema() datasourceSchema.Schema { + description := "script module" + return datasourceSchema.Schema{ + Description: "Provides information about existing script modules.", + Attributes: map[string]datasourceSchema.Attribute{ + "id": GetIdDatasourceSchema(true), + "space_id": GetSpaceIdDatasourceSchema(description, false), + "ids": util.GetQueryIDsDatasourceSchema(), + "partial_name": util.GetQueryPartialNameDatasourceSchema(), + "skip": util.GetQuerySkipDatasourceSchema(), + "take": util.GetQueryTakeDatasourceSchema(), + "script_modules": datasourceSchema.ListNestedAttribute{ + Computed: true, + NestedObject: datasourceSchema.NestedAttributeObject{ + Attributes: GetScriptModuleDatasourceSchema(), + }, + }, + }, + } +} + +func GetScriptModuleDatasourceSchema() map[string]datasourceSchema.Attribute { + return map[string]datasourceSchema.Attribute{ + "description": GetReadonlyDescriptionDatasourceSchema("script module"), + "id": GetIdDatasourceSchema(true), + "name": GetReadonlyNameDatasourceSchema(), + "space_id": GetSpaceIdDatasourceSchema("Script Module", true), + "variable_set_id": datasourceSchema.StringAttribute{ + Computed: true, + Description: "The variable set ID for this script module.", + }, + "script": datasourceSchema.ListNestedAttribute{ + Description: "The script associated with this script module.", + Computed: true, + NestedObject: datasourceSchema.NestedAttributeObject{ + Attributes: map[string]datasourceSchema.Attribute{ + "body": datasourceSchema.StringAttribute{ + Description: "The body of this script module.", + Computed: true, + }, + "syntax": datasourceSchema.StringAttribute{ + Description: "The syntax of the script. Valid types are `Bash`, `CSharp`, `FSharp`, `PowerShell`, or `Python`.", + Computed: true, + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive( + "Bash", + "CSharp", + "FSharp", + "PowerShell", + "Python"), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + listvalidator.SizeAtLeast(1), + }, + }, + } +} + +func FlattenScriptModule(scriptModule *variables.ScriptModule) attr.Value { + attrs := map[string]attr.Value{ + "description": types.StringValue(scriptModule.Description), + "id": types.StringValue(scriptModule.GetID()), + "name": types.StringValue(scriptModule.Name), + "script": types.ListValueMust(ScriptObjectType(), flattenScript(scriptModule)), + "space_id": types.StringValue(scriptModule.SpaceID), + "variable_set_id": types.StringValue(scriptModule.VariableSetID), + } + + return types.ObjectValueMust(ScriptModuleObjectType(), attrs) +} + +func ScriptObjectType() types.ObjectType { + return types.ObjectType{AttrTypes: map[string]attr.Type{ + "body": types.StringType, + "syntax": types.StringType, + }} +} + +func ScriptModuleObjectType() map[string]attr.Type { + return map[string]attr.Type{ + "description": types.StringType, + "id": types.StringType, + "name": types.StringType, + "space_id": types.StringType, + "variable_set_id": types.StringType, + "script": types.ListType{ElemType: ScriptObjectType()}, + } +} + +func GetScriptModuleSchemaBlock() map[string]resourceSchema.Block { + return map[string]resourceSchema.Block{ + "script": resourceSchema.ListNestedBlock{ + Description: "The script associated with this script module.", + NestedObject: resourceSchema.NestedBlockObject{ + Attributes: map[string]resourceSchema.Attribute{ + "body": resourceSchema.StringAttribute{ + Description: "The body of this script module.", + Required: true, + }, + "syntax": resourceSchema.StringAttribute{ + Description: "The syntax of the script. Valid types are `Bash`, `CSharp`, `FSharp`, `PowerShell`, or `Python`.", + Required: true, + Validators: []validator.String{ + stringvalidator.OneOfCaseInsensitive( + "Bash", + "CSharp", + "FSharp", + "PowerShell", + "Python"), + }, + }, + }, + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + listvalidator.SizeAtLeast(1), + }, + }, + } +} + +func GetScriptModuleResourceSchema() map[string]resourceSchema.Attribute { + return map[string]resourceSchema.Attribute{ + "description": GetDescriptionResourceSchema("script module"), + "id": GetIdResourceSchema(), + "name": GetNameResourceSchema(true), + "space_id": GetSpaceIdResourceSchema("Script Module"), + "variable_set_id": resourceSchema.StringAttribute{ + Computed: true, + Description: "The variable set ID for this script module.", + Optional: true, + }, + } +} + +func MapFromScriptModuleToState(data *ScriptModuleResourceModel) *variables.ScriptModule { + name := data.Name.ValueString() + scriptModule := variables.NewScriptModule(name) + scriptModule.ID = data.ID.ValueString() + scriptModule.Description = data.Description.ValueString() + // We enforce on the schema a single required script + scriptDetails := data.Script.Elements()[0].(types.Object).Attributes() + scriptModule.Syntax = scriptDetails["syntax"].(types.String).ValueString() + scriptModule.ScriptBody = scriptDetails["body"].(types.String).ValueString() + scriptModule.SpaceID = data.SpaceID.ValueString() + scriptModule.VariableSetID = data.VariableSetId.ValueString() + + return scriptModule +} + +func flattenScript(scriptModule *variables.ScriptModule) []attr.Value { + return []attr.Value{ + types.ObjectValueMust(map[string]attr.Type{ + "body": types.StringType, + "syntax": types.StringType, + }, map[string]attr.Value{ + "body": types.StringValue(scriptModule.ScriptBody), + "syntax": types.StringValue(scriptModule.Syntax), + }), + } +} + +func MapToScriptModuleFromState(data *ScriptModuleResourceModel, scriptModule *variables.ScriptModule) { + data.Description = types.StringValue(scriptModule.Description) + data.Name = types.StringValue(scriptModule.Name) + data.SpaceID = types.StringValue(scriptModule.SpaceID) + data.VariableSetId = types.StringValue(scriptModule.VariableSetID) + data.ID = types.StringValue(scriptModule.ID) + + flattenScript(scriptModule) + + var script, _ = types.ListValue(ScriptObjectType(), flattenScript(scriptModule)) + data.Script = script +}