diff --git a/docs/data-sources/channels.md b/docs/data-sources/channels.md index bf1546f4f..0e75679a9 100644 --- a/docs/data-sources/channels.md +++ b/docs/data-sources/channels.md @@ -43,6 +43,8 @@ data "octopusdeploy_channels" "example" { Read-Only: - `description` (String) The description of this channel. +- `git_reference_rules` (List of String) List of rules to restrict which Git references can be used with this channel when creating releases for version controlled projects. References must be fully qualified e.g. `refs/heads/main`. Supports glob patten syntax. +- `git_resource_rules` (List of Object) List of rules to restrict which Git resources can be used with this channel when creating releases with external Git resources. Resources must be fully qualified e.g. `refs/heads/main`. Supports glob patten syntax. (see [below for nested schema](#nestedatt--channels--git_resource_rules)) - `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. @@ -52,6 +54,25 @@ Read-Only: - `space_id` (String) The space ID associated with this resource. - `tenant_tags` (List of String) A list of tenant tags associated with this resource. + +### Nested Schema for `channels.git_resource_rules` + +Read-Only: + +- `git_dependency_actions` (List of Object) (see [below for nested schema](#nestedobjatt--channels--git_resource_rules--git_dependency_actions)) +- `id` (String) +- `rules` (List of String) + + +### Nested Schema for `channels.git_resource_rules.git_dependency_actions` + +Read-Only: + +- `deployment_action_slug` (String) +- `git_dependency_name` (String) + + + ### Nested Schema for `channels.rule` 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/channel.md b/docs/resources/channel.md index ca879b8d9..1fa5be29b 100644 --- a/docs/resources/channel.md +++ b/docs/resources/channel.md @@ -30,6 +30,8 @@ resource "octopusdeploy_channel" "example" { ### Optional - `description` (String) The description of this channel. +- `git_reference_rules` (List of String) List of rules to restrict which Git references can be used with this channel when creating releases for version controlled projects. References must be fully qualified e.g. `refs/heads/main`. Supports glob patten syntax. +- `git_resource_rules` (Block List) List of rules to restrict which Git resources can be used with this channel when creating releases with external Git resources. Resources must be fully qualified e.g. `refs/heads/main`. Supports glob patten syntax. (see [below for nested schema](#nestedblock--git_resource_rules)) - `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. @@ -37,6 +39,28 @@ resource "octopusdeploy_channel" "example" { - `space_id` (String) The space ID associated with this resource. - `tenant_tags` (List of String) A list of tenant tags associated with this resource. + +### Nested Schema for `git_resource_rules` + +Required: + +- `git_dependency_actions` (Block List, Min: 1) (see [below for nested schema](#nestedblock--git_resource_rules--git_dependency_actions)) + +Optional: + +- `id` (String) The unique ID for this resource. +- `rules` (List of String) + + +### Nested Schema for `git_resource_rules.git_dependency_actions` + +Optional: + +- `deployment_action_slug` (String) +- `git_dependency_name` (String) + + + ### Nested Schema for `rule` diff --git a/go.mod b/go.mod index f9bdcfde4..4c75feed5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/OctopusDeploy/terraform-provider-octopusdeploy go 1.21 require ( - github.com/OctopusDeploy/go-octopusdeploy/v2 v2.43.0 + github.com/OctopusDeploy/go-octopusdeploy/v2 v2.44.1 github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240725054341-2848f54d101e github.com/google/uuid v1.6.0 github.com/gruntwork-io/terratest v0.41.11 diff --git a/go.sum b/go.sum index d50b01763..b8bdbf8a1 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exY github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.43.0 h1:fYwGBqG88xy3qHp5j1ySCztdqfw2NLfg2yp0N3XcBYg= github.com/OctopusDeploy/go-octopusdeploy/v2 v2.43.0/go.mod h1:GZmFu6LmN8Yg0tEoZx3ytk9FnaH+84cWm7u5TdWZC6E= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.44.1 h1:e8aGPNAXEBr/PRSTghyE1FgKERJXYvCPh/12C11kmbE= +github.com/OctopusDeploy/go-octopusdeploy/v2 v2.44.1/go.mod h1:GZmFu6LmN8Yg0tEoZx3ytk9FnaH+84cWm7u5TdWZC6E= 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/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= diff --git a/octopusdeploy/schema_channel.go b/octopusdeploy/schema_channel.go index 88aa0bda9..13f0ecbeb 100644 --- a/octopusdeploy/schema_channel.go +++ b/octopusdeploy/schema_channel.go @@ -43,6 +43,18 @@ func expandChannel(d *schema.ResourceData) *channels.Channel { } } + if v, ok := d.GetOk("git_reference_rules"); ok { + channel.GitReferenceRules = expandArray(v.([]interface{})) + } + + if v, ok := d.GetOk("git_resource_rules"); ok { + channelGitResourceRules := v.([]interface{}) + for _, channelGitResourceRule := range channelGitResourceRules { + rule := expandChannelGitResourceRules(channelGitResourceRule.(map[string]interface{})) + channel.GitResourceRules = append(channel.GitResourceRules, rule) + } + } + return channel } @@ -52,15 +64,17 @@ func flattenChannel(channel *channels.Channel) map[string]interface{} { } return map[string]interface{}{ - "description": channel.Description, - "id": channel.GetID(), - "is_default": channel.IsDefault, - "lifecycle_id": channel.LifecycleID, - "name": channel.Name, - "project_id": channel.ProjectID, - "rule": flattenChannelRules(channel.Rules), - "space_id": channel.SpaceID, - "tenant_tags": channel.TenantTags, + "description": channel.Description, + "id": channel.GetID(), + "is_default": channel.IsDefault, + "lifecycle_id": channel.LifecycleID, + "name": channel.Name, + "project_id": channel.ProjectID, + "rule": flattenChannelRules(channel.Rules), + "space_id": channel.SpaceID, + "tenant_tags": channel.TenantTags, + "git_reference_rules": flattenArray(channel.GitReferenceRules), + "git_resource_rules": flattenChannelGitResourceRules(channel.GitResourceRules), } } @@ -112,6 +126,18 @@ func getChannelSchema() map[string]*schema.Schema { }, "space_id": getSpaceIDSchema(), "tenant_tags": getTenantTagsSchema(), + "git_reference_rules": { + Description: "List of rules to restrict which Git references can be used with this channel when creating releases for version controlled projects. References must be fully qualified e.g. `refs/heads/main`. Supports glob patten syntax.", + Optional: true, + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "git_resource_rules": { + Description: "List of rules to restrict which Git resources can be used with this channel when creating releases with external Git resources. Resources must be fully qualified e.g. `refs/heads/main`. Supports glob patten syntax.", + Optional: true, + Type: schema.TypeList, + Elem: &schema.Resource{Schema: getChannelGitResourceRuleSchema()}, + }, } } @@ -131,5 +157,13 @@ func setChannel(ctx context.Context, d *schema.ResourceData, channel *channels.C return fmt.Errorf("error setting tenant_tags: %s", err) } + if err := d.Set("git_reference_rules", channel.GitReferenceRules); err != nil { + return fmt.Errorf("error setting git_reference_rules: %s", err) + } + + if err := d.Set("git_resource_rules", flattenChannelGitResourceRules(channel.GitResourceRules)); err != nil { + return fmt.Errorf("error setting git_resource_rules: %s", err) + } + return nil } diff --git a/octopusdeploy/schema_channel_git_resource_rule.go b/octopusdeploy/schema_channel_git_resource_rule.go new file mode 100644 index 000000000..e27d3283e --- /dev/null +++ b/octopusdeploy/schema_channel_git_resource_rule.go @@ -0,0 +1,51 @@ +package octopusdeploy + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/channels" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func expandChannelGitResourceRules(ChannelGitResourceRule map[string]interface{}) channels.ChannelGitResourceRule { + if len(ChannelGitResourceRule) == 0 { + return channels.ChannelGitResourceRule{} + } + + return channels.ChannelGitResourceRule{ + Id: ChannelGitResourceRule["id"].(string), + GitDependencyActions: expandDeploymentActionGitDependencies(ChannelGitResourceRule["git_dependency_actions"]), + Rules: ChannelGitResourceRule["rules"].([]string), + } +} + +func flattenChannelGitResourceRules(ChannelGitResourceRules []channels.ChannelGitResourceRule) []map[string]interface{} { + if len(ChannelGitResourceRules) == 0 { + return []map[string]interface{}{} + } + + var flattenedRules = make([]map[string]interface{}, len(ChannelGitResourceRules)) + for key, ChannelGitResourceRule := range ChannelGitResourceRules { + flattenedRules[key] = map[string]interface{}{ + "id": ChannelGitResourceRule.Id, + "git_dependency_actions": flattenDeploymentActionGitDependencies(ChannelGitResourceRule.GitDependencyActions), + "rules": ChannelGitResourceRule.Rules, + } + } + + return flattenedRules +} + +func getChannelGitResourceRuleSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "id": getIDSchema(), + "git_dependency_actions": { + Elem: &schema.Resource{Schema: getDeploymentActionGitDependencySchema()}, + Required: true, + Type: schema.TypeList, + }, + "rules": { + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Type: schema.TypeList, + }, + } +} diff --git a/octopusdeploy/schema_deployment_action_git_dependency.go b/octopusdeploy/schema_deployment_action_git_dependency.go new file mode 100644 index 000000000..a66811eb7 --- /dev/null +++ b/octopusdeploy/schema_deployment_action_git_dependency.go @@ -0,0 +1,51 @@ +package octopusdeploy + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/gitdependencies" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func flattenDeploymentActionGitDependencies(deploymentActionGitDependencies []gitdependencies.DeploymentActionGitDependency) []interface{} { + if len(deploymentActionGitDependencies) == 0 { + return nil + } + + var flattenedDeploymentActionPackages []interface{} + for _, v := range deploymentActionGitDependencies { + flattenedDeploymentActionPackage := map[string]interface{}{ + "deployment_action_slug": v.DeploymentActionSlug, + "git_dependency_name": v.GitDependencyName, + } + flattenedDeploymentActionPackages = append(flattenedDeploymentActionPackages, flattenedDeploymentActionPackage) + } + return flattenedDeploymentActionPackages +} + +func expandDeploymentActionGitDependencies(values interface{}) []gitdependencies.DeploymentActionGitDependency { + if values == nil { + return nil + } + + var gitDependencies []gitdependencies.DeploymentActionGitDependency + for _, v := range values.([]interface{}) { + flattenedMap := v.(map[string]interface{}) + gitDependencies = append(gitDependencies, gitdependencies.DeploymentActionGitDependency{ + DeploymentActionSlug: flattenedMap["deployment_action_slug"].(string), + GitDependencyName: flattenedMap["git_dependency_name"].(string), + }) + } + return gitDependencies +} + +func getDeploymentActionGitDependencySchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "deployment_action_slug": { + Optional: true, + Type: schema.TypeString, + }, + "git_dependency_name": { + Optional: true, + Type: schema.TypeString, + }, + } +} diff --git a/octopusdeploy/schema_deployment_action_git_resource_test.go b/octopusdeploy/schema_deployment_action_git_resource_test.go new file mode 100644 index 000000000..7b9e38cd7 --- /dev/null +++ b/octopusdeploy/schema_deployment_action_git_resource_test.go @@ -0,0 +1,96 @@ +package octopusdeploy + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/channels" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/gitdependencies" + "reflect" + "testing" +) + +func TestExpandChannelGitResourceRules_WithValidData_ReturnsExpectedResult(t *testing.T) { + input := map[string]interface{}{ + "id": "rule-1", + "git_dependency_actions": []interface{}{ + map[string]interface{}{ + "deployment_action_slug": "deploy-action-1", + "git_dependency_name": "", + }, + }, + "rules": []string{"rule1", "rule2"}, + } + + expected := channels.ChannelGitResourceRule{ + Id: "rule-1", + GitDependencyActions: []gitdependencies.DeploymentActionGitDependency{ + { + DeploymentActionSlug: "deploy-action-1", + GitDependencyName: "", + }, + }, + Rules: []string{"rule1", "rule2"}, + } + + actual := expandChannelGitResourceRules(input) + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("Expected %+v, got %+v", expected, actual) + } +} + +func TestExpandChannelGitResourceRules_WithEmptyData_ReturnsEmptyStruct(t *testing.T) { + input := map[string]interface{}{} + + expected := channels.ChannelGitResourceRule{} + + actual := expandChannelGitResourceRules(input) + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("Expected %+v, got %+v", expected, actual) + } +} + +func TestFlattenChannelGitResourceRules_WithValidData_ReturnsExpectedMap(t *testing.T) { + input := []channels.ChannelGitResourceRule{ + { + Id: "rule-1", + GitDependencyActions: []gitdependencies.DeploymentActionGitDependency{ + { + DeploymentActionSlug: "deploy-action-1", + GitDependencyName: "ref-1", + }, + }, + Rules: []string{"rule1", "rule2"}, + }, + } + + expected := []map[string]interface{}{ + { + "id": "rule-1", + "git_dependency_actions": []interface{}{ + map[string]interface{}{ + "deployment_action_slug": "deploy-action-1", + "git_dependency_name": "ref-1", + }, + }, + "rules": []string{"rule1", "rule2"}, + }, + } + + actual := flattenChannelGitResourceRules(input) + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("Expected %+v, got %+v", expected, actual) + } +} + +func TestFlattenChannelGitResourceRules_WithEmptyData_ReturnsEmptySlice(t *testing.T) { + input := []channels.ChannelGitResourceRule{} + + expected := []map[string]interface{}{} + + actual := flattenChannelGitResourceRules(input) + + if !reflect.DeepEqual(actual, expected) { + t.Errorf("Expected %+v, got %+v", expected, actual) + } +}