diff --git a/go.mod b/go.mod index 36a579184..09874bbf9 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,13 @@ module github.com/OctopusDeploy/terraform-provider-octopusdeploy require ( - github.com/OctopusDeploy/go-octopusdeploy v0.0.0-20190101071024-032701337fa6 + github.com/OctopusDeploy/go-octopusdeploy v1.0.0 github.com/apparentlymart/go-cidr v1.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/aws/aws-sdk-go v1.15.53 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/dghubble/sling v1.1.0 // indirect github.com/go-ini/ini v1.38.3 // indirect - github.com/go-playground/locales v0.12.1 // indirect - github.com/go-playground/universal-translator v0.16.0 // indirect github.com/google/go-querystring v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.0 // indirect github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 // indirect @@ -39,7 +36,6 @@ require ( github.com/zclconf/go-cty v0.0.0-20181011210859-1f835aab79c2 // indirect google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f // indirect google.golang.org/grpc v1.15.0 // indirect - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.21.0 // indirect gopkg.in/ini.v1 v1.42.0 // indirect ) diff --git a/go.sum b/go.sum index b178950f2..2ae8d573b 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/DHowett/go-plist v0.0.0-20180609054337-500bd5b9081b/go.mod h1:5paT5ZDrOm8eAJPem2Bd+q3FTi3Gxm/U4tb2tH8YIUQ= -github.com/OctopusDeploy/go-octopusdeploy v0.0.0-20190101071024-032701337fa6 h1:KZHt2zDW22EbhfTvsZLzWRzn/5Kb8Jhnnk27wJQr/lY= -github.com/OctopusDeploy/go-octopusdeploy v0.0.0-20190101071024-032701337fa6/go.mod h1:MLm+0h6BI+oIU1ucONLo9AiN3YklMo6dzc7aAH2bgYo= +github.com/OctopusDeploy/go-octopusdeploy v1.0.0 h1:JweXaa6zTIv8NOpLDLomzceVGV+v4L/zrOHN5DOZykc= +github.com/OctopusDeploy/go-octopusdeploy v1.0.0/go.mod h1:WyBvcyhFPMULbZwFFWOmt6/irqrfZ/WqCy7ufa8/CGE= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-cidr v1.0.0 h1:lGDvXx8Lv9QHjrAVP7jyzleG4F9+FkRhJcEsDFxeb8w= @@ -22,6 +22,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dghubble/sling v1.1.0 h1:DLu20Bq2qsB9cI5Hldaxj+TMPEaPpPE8IR2kvD22Atg= @@ -45,6 +46,7 @@ github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= @@ -125,6 +127,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.2 h1:fS9GkqLN9DIpHg9j3fAPHdj5P3LhzxuoSybQd1v26IE= github.com/posener/complete v1.1.2/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -170,6 +173,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.19.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/go-playground/validator.v9 v9.21.0 h1:wSDJGBpQBYC1wLpVnGHLmshm2JicoSNdrb38Zj+8yHI= gopkg.in/go-playground/validator.v9 v9.21.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= diff --git a/octopusdeploy/apply_terraform_action.go b/octopusdeploy/apply_terraform_action.go new file mode 100644 index 000000000..f25be93eb --- /dev/null +++ b/octopusdeploy/apply_terraform_action.go @@ -0,0 +1,34 @@ +package octopusdeploy + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func getApplyTerraformActionSchema() *schema.Schema { + + actionSchema, element := getCommonDeploymentActionSchema() + addExecutionLocationSchema(element) + addPrimaryPackageSchema(element, false) + + element.Schema["additional_init_params"] = &schema.Schema{ + Type: schema.TypeString, + Description: "Additional parameters passed to the init command", + Optional: true, + } + + return actionSchema +} + +func buildApplyTerraformActionResource(tfAction map[string]interface{}) octopusdeploy.DeploymentAction { + resource := buildDeploymentActionResource(tfAction) + + resource.ActionType = "Octopus.TerraformApply" + resource.Properties["Octopus.Action.Terraform.AdditionalInitParams"] = tfAction["additional_init_params"].(string) + resource.Properties["Octopus.Action.Terraform.AllowPluginDownloads"] = "True" + resource.Properties["Octopus.Action.Terraform.ManagedAccount"] = "None" + + resource.Properties["Octopus.Action.Script.ScriptSource"] = "Package" + + return resource +} diff --git a/octopusdeploy/apply_terraform_action_test.go b/octopusdeploy/apply_terraform_action_test.go new file mode 100644 index 000000000..b8dd20251 --- /dev/null +++ b/octopusdeploy/apply_terraform_action_test.go @@ -0,0 +1,65 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployApplyTerraformAction(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOctopusDeployDeploymentProcessDestroy, + Steps: []resource.TestStep{ + { + Config: testAccApplyTerraformAction(), + Check: resource.ComposeTestCheckFunc( + testAccCheckApplyTerraformAction(), + ), + }, + }, + }) +} + +func testAccApplyTerraformAction() string { + return testAccBuildTestAction(` + apply_terraform_action { + name = "Apply Terraform" + run_on_server = true + + primary_package { + package_id = "MyPackage" + feed_id = "feeds-builtin" + } + + additional_init_params = "Init params" + } + `) +} + +func testAccCheckApplyTerraformAction() resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + + process, err := getDeploymentProcess(s, client) + if err != nil { + return err + } + + action := process.Steps[0].Actions[0] + + if action.ActionType != "Octopus.TerraformApply" { + return fmt.Errorf("Action type is incorrect: %s", action.ActionType) + } + + if action.Properties["Octopus.Action.Terraform.AdditionalInitParams"] != "Init params" { + return fmt.Errorf("AdditionalInitParams is incorrect: %s", action.Properties["Octopus.Action.Terraform.AdditionalInitParams"]) + } + + return nil + } +} diff --git a/octopusdeploy/data_environment.go b/octopusdeploy/data_environment.go index 257f65fdb..f991e626e 100644 --- a/octopusdeploy/data_environment.go +++ b/octopusdeploy/data_environment.go @@ -24,6 +24,10 @@ func dataEnvironment() *schema.Resource { Type: schema.TypeBool, Computed: true, }, + "allow_dynamic_infrastructure": { + Type: schema.TypeBool, + Computed: true, + }, }, } } @@ -47,6 +51,7 @@ func dataEnvironmentReadByName(d *schema.ResourceData, m interface{}) error { d.Set("name", env.Name) d.Set("description", env.Description) d.Set("use_guided_failure", env.UseGuidedFailure) + d.Set("allow_dynamic_infrastructure", env.AllowDynamicInfrastructure) return nil } diff --git a/octopusdeploy/deploy_kubernetes_secret_action.go b/octopusdeploy/deploy_kubernetes_secret_action.go new file mode 100644 index 000000000..196f44c83 --- /dev/null +++ b/octopusdeploy/deploy_kubernetes_secret_action.go @@ -0,0 +1,61 @@ +package octopusdeploy + +import ( + "encoding/json" + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func getDeployKubernetesSecretActionSchema() *schema.Schema { + + actionSchema, element := getCommonDeploymentActionSchema() + addExecutionLocationSchema(element) + element.Schema["secret_name"] = &schema.Schema{ + Type: schema.TypeString, + Description: "The name of the secret resource", + Required: true, + } + + element.Schema["secret_values"] = &schema.Schema{ + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } + + return actionSchema +} + +func buildDeployKubernetesSecretActionResource(tfAction map[string]interface{}) octopusdeploy.DeploymentAction { + resource := buildDeploymentActionResource(tfAction) + + resource.ActionType = "Octopus.KubernetesDeploySecret" + + resource.Properties["Octopus.Action.KubernetesContainers.SecretName"] = tfAction["secret_name"].(string) + + if tfSecretValues, ok := tfAction["secret_values"]; ok { + + secretValues := make(map[string]string) + + for _, tfSecretValue := range tfSecretValues.([]interface{}) { + tfSecretValueTyped := tfSecretValue.(map[string]interface{}) + secretValues[tfSecretValueTyped["key"].(string)] = tfSecretValueTyped["value"].(string) + } + + j, _ := json.Marshal(secretValues) + + resource.Properties["Octopus.Action.KubernetesContainers.SecretValues"] = string(j) + } + + return resource +} diff --git a/octopusdeploy/deploy_kubernetes_secret_action_test.go b/octopusdeploy/deploy_kubernetes_secret_action_test.go new file mode 100644 index 000000000..da550874a --- /dev/null +++ b/octopusdeploy/deploy_kubernetes_secret_action_test.go @@ -0,0 +1,73 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployDeployKuberentesSecretAction(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOctopusDeployDeploymentProcessDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDeployKuberentesSecretAction(), + Check: resource.ComposeTestCheckFunc( + testAccCheckDeployKuberentesSecretAction(), + ), + }, + }, + }) +} + +func testAccDeployKuberentesSecretAction() string { + return testAccBuildTestAction(` + deploy_kubernetes_secret_action { + name = "Run Script" + run_on_server = true + + secret_name = "secret name" + + secret_values = [{ + key = "key" + value = "value" + }, { + key = "key1" + value = "value1" + } + ] + } + `) +} + +func testAccCheckDeployKuberentesSecretAction() resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + + process, err := getDeploymentProcess(s, client) + if err != nil { + return err + } + + action := process.Steps[0].Actions[0] + + if action.ActionType != "Octopus.KubernetesDeploySecret" { + return fmt.Errorf("Action type is incorrect: %s", action.ActionType) + } + + if action.Properties["Octopus.Action.KubernetesContainers.SecretName"] != "secret name" { + return fmt.Errorf("SecretName is incorrect: %s", action.Properties["Octopus.Action.KubernetesContainers.SecretName"]) + } + + if action.Properties["Octopus.Action.KubernetesContainers.SecretValues"] != `{"key":"value","key1":"value1"}` { + return fmt.Errorf("SecretName is incorrect: %s", action.Properties["Octopus.Action.KubernetesContainers.SecretName"]) + } + + return nil + } +} diff --git a/octopusdeploy/deploy_windows_service_action_test.go b/octopusdeploy/deploy_windows_service_action_test.go index 824d8ffbc..3f3eb42da 100644 --- a/octopusdeploy/deploy_windows_service_action_test.go +++ b/octopusdeploy/deploy_windows_service_action_test.go @@ -42,7 +42,7 @@ func TestAccOctopusDeployWindowsServiceFeature(t *testing.T) { } func testAccDeployWindowsServiceAction() string { - return testAccBuildTestActionTerraform(` + return testAccBuildTestAction(` deploy_windows_service_action { name = "Test" @@ -65,7 +65,7 @@ func testAccDeployWindowsServiceAction() string { } func testAccWindowsServiceFeature() string { - return testAccBuildTestActionTerraform(` + return testAccBuildTestAction(` deploy_package_action { name = "Test" diff --git a/octopusdeploy/deployment_action.go b/octopusdeploy/deployment_action.go index abaed499e..206ab9b19 100644 --- a/octopusdeploy/deployment_action.go +++ b/octopusdeploy/deployment_action.go @@ -9,6 +9,8 @@ import ( func getDeploymentActionSchema() *schema.Schema { actionSchema, element := getCommonDeploymentActionSchema() addExecutionLocationSchema(element) + addActionTypeSchema(element) + addExecutionLocationSchema(element) element.Schema["action_type"] = &schema.Schema{ Type: schema.TypeString, Description: "The type of action", @@ -94,6 +96,14 @@ func addExecutionLocationSchema(element *schema.Resource) { } } +func addActionTypeSchema(element *schema.Resource) { + element.Schema["action_type"] = &schema.Schema{ + Type: schema.TypeString, + Description: "The type of action", + Required: true, + } +} + func addWorkerPoolSchema(element *schema.Resource) { element.Schema["worker_pool_id"] = &schema.Schema{ Type: schema.TypeString, diff --git a/octopusdeploy/deployment_process_test.go b/octopusdeploy/deployment_process_test.go index d99b892a2..fe6db46db 100644 --- a/octopusdeploy/deployment_process_test.go +++ b/octopusdeploy/deployment_process_test.go @@ -85,6 +85,19 @@ func testAccDeploymentProcessBasic() string { } + package { + name = "ThePackage2" + package_id = "MyPackage2" + feed_id = "feeds-builtin" + acquisition_location = "NotAcquired" + extract_during_deployment = true + + property { + key = "WhatIsThis" + value = "Dunno" + } + } + property { key = "Octopus.Action.Script.ScriptFileName" value = "Run.ps132" @@ -117,7 +130,7 @@ func testAccDeploymentProcessBasic() string { ` } -func testAccBuildTestActionTerraform(action string) string { +func testAccBuildTestAction(action string) string { return fmt.Sprintf(` resource "octopusdeploy_lifecycle" "test" { name = "Test Lifecycle" diff --git a/octopusdeploy/deployment_step.go b/octopusdeploy/deployment_step.go index 9c8c8cc98..9db4d8b43 100644 --- a/octopusdeploy/deployment_step.go +++ b/octopusdeploy/deployment_step.go @@ -68,10 +68,14 @@ func getDeploymentStepSchema() *schema.Schema { Description: "The maximum number of targets to deploy to simultaneously", Optional: true, }, - "action": getDeploymentActionSchema(), - "manual_intervention_action": getManualInterventionActionSchema(), - "deploy_package_action": getDeployPackageAction(), - "deploy_windows_service_action": getDeployWindowsServiceActionSchema(), + "action": getDeploymentActionSchema(), + "manual_intervention_action": getManualInterventionActionSchema(), + "apply_terraform_action": getApplyTerraformActionSchema(), + "deploy_package_action": getDeployPackageAction(), + "deploy_windows_service_action": getDeployWindowsServiceActionSchema(), + "run_script_action": getRunScriptActionSchema(), + "run_kubectl_script_action": getRunRunKubectlScriptSchema(), + "deploy_kubernetes_secret_action": getDeployKubernetesSecretActionSchema(), }, }, } @@ -115,6 +119,13 @@ func buildDeploymentStepResource(tfStep map[string]interface{}) octopusdeploy.De } } + if attr, ok := tfStep["apply_terraform_action"]; ok { + for _, tfAction := range attr.([]interface{}) { + action := buildApplyTerraformActionResource(tfAction.(map[string]interface{})) + step.Actions = append(step.Actions, action) + } + } + if attr, ok := tfStep["deploy_package_action"]; ok { for _, tfAction := range attr.([]interface{}) { action := buildDeployPackageActionResource(tfAction.(map[string]interface{})) @@ -129,5 +140,26 @@ func buildDeploymentStepResource(tfStep map[string]interface{}) octopusdeploy.De } } + if attr, ok := tfStep["run_script_action"]; ok { + for _, tfAction := range attr.([]interface{}) { + action := buildRunScriptActionResource(tfAction.(map[string]interface{})) + step.Actions = append(step.Actions, action) + } + } + + if attr, ok := tfStep["run_kubectl_script_action"]; ok { + for _, tfAction := range attr.([]interface{}) { + action := buildRunKubectlScriptActionResource(tfAction.(map[string]interface{})) + step.Actions = append(step.Actions, action) + } + } + + if attr, ok := tfStep["deploy_kubernetes_secret_action"]; ok { + for _, tfAction := range attr.([]interface{}) { + action := buildDeployKubernetesSecretActionResource(tfAction.(map[string]interface{})) + step.Actions = append(step.Actions, action) + } + } + return step } diff --git a/octopusdeploy/manual_intervention_action_test.go b/octopusdeploy/manual_intervention_action_test.go index 908ea6fb4..00396186d 100644 --- a/octopusdeploy/manual_intervention_action_test.go +++ b/octopusdeploy/manual_intervention_action_test.go @@ -26,7 +26,7 @@ func TestAccOctopusDeployManualInterventionAction(t *testing.T) { } func testAccManualInterventionAction() string { - return testAccBuildTestActionTerraform(` + return testAccBuildTestAction(` manual_intervention_action { name = "Test" instructions = "Approve Me" diff --git a/octopusdeploy/package_reference.go b/octopusdeploy/package_reference.go index bc81eaddd..2aa23fa7e 100644 --- a/octopusdeploy/package_reference.go +++ b/octopusdeploy/package_reference.go @@ -7,6 +7,7 @@ import ( func addPrimaryPackageSchema(element *schema.Resource, required bool) { element.Schema["primary_package"] = getPackageSchema(required) + element.Schema["primary_package"].MaxItems = 1 } func addPackagesSchema(element *schema.Resource, primaryIsRequired bool) { @@ -15,11 +16,13 @@ func addPackagesSchema(element *schema.Resource, primaryIsRequired bool) { element.Schema["package"] = getPackageSchema(false) packageElementSchema := element.Schema["package"].Elem.(*schema.Resource).Schema + packageElementSchema["name"] = &schema.Schema{ Type: schema.TypeString, Description: "The name of the package", Required: true, } + packageElementSchema["extract_during_deployment"] = &schema.Schema{ Type: schema.TypeString, Description: "Whether to extract the package during deployment", @@ -34,7 +37,6 @@ func getPackageSchema(required bool) *schema.Schema { Type: schema.TypeSet, Required: required, Optional: !required, - MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "package_id": { diff --git a/octopusdeploy/provider.go b/octopusdeploy/provider.go index 2b921e5f2..3ffc86679 100644 --- a/octopusdeploy/provider.go +++ b/octopusdeploy/provider.go @@ -24,11 +24,14 @@ func Provider() terraform.ResourceProvider { "octopusdeploy_project_group": resourceProjectGroup(), "octopusdeploy_project_deployment_target_trigger": resourceProjectDeploymentTargetTrigger(), "octopusdeploy_environment": resourceEnvironment(), + "octopusdeploy_account": resourceAccount(), + "octopusdeploy_feed": resourceFeed(), "octopusdeploy_variable": resourceVariable(), "octopusdeploy_machine": resourceMachine(), "octopusdeploy_library_variable_set": resourceLibraryVariableSet(), "octopusdeploy_lifecycle": resourceLifecycle(), "octopusdeploy_deployment_process": resourceDeploymentProcess(), + "octopusdeploy_tag_set": resourceTagSet(), }, Schema: map[string]*schema.Schema{ "address": { diff --git a/octopusdeploy/resource_account.go b/octopusdeploy/resource_account.go new file mode 100644 index 000000000..7e788c806 --- /dev/null +++ b/octopusdeploy/resource_account.go @@ -0,0 +1,196 @@ +package octopusdeploy + +import ( + "fmt" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAccount() *schema.Resource { + return &schema.Resource{ + Create: resourceAccountCreate, + Read: resourceAccountRead, + Update: resourceAccountUpdate, + Delete: resourceAccountDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "account_type": { + Type: schema.TypeString, + Required: true, + }, + "client_id": { + Type: schema.TypeString, + Required: true, + }, + "tenant_id": { + Type: schema.TypeString, + Required: true, + }, + "subscription_id": { + Type: schema.TypeString, + Required: true, + }, + "client_secret": { + Type: schema.TypeString, + Required: true, + }, + "tenant_tags": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "tenanted_deployment_participation": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateValueFunc([]string{ + "Untenanted", + "TenantedOrUntenanted", + "Tenanted", + }), + }, + }, + } +} + +func resourceAccountRead(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + accountId := d.Id() + account, err := client.Account.Get(accountId) + + if err == octopusdeploy.ErrItemNotFound { + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading account %s: %s", accountId, err.Error()) + } + + d.Set("name", account.Name) + d.Set("account_type", account.AccountType) + d.Set("client_id", account.ClientId) + d.Set("tenant_id", account.TenantId) + d.Set("subscription_id", account.SubscriptionNumber) + d.Set("client_secret", account.Password) + d.Set("tenant_tags", account.TenantTags) + d.Set("tenanted_deployment_participation", account.TenantedDeploymentParticipation) + + return nil +} + +func buildAccountResource(d *schema.ResourceData) *octopusdeploy.Account { + accountName := d.Get("name").(string) + + var accountType string + var clientId string + var tenantId string + var subscriptionId string + var clientSecret string + var tenantTags []string + var tenantedDeploymentParticipation string + + accountTypeInterface, ok := d.GetOk("account_type") + if ok { + accountType = accountTypeInterface.(string) + } + + clientIdInterface, ok := d.GetOk("client_id") + if ok { + clientId = clientIdInterface.(string) + } + + tenantIdInterface, ok := d.GetOk("tenant_id") + if ok { + tenantId = tenantIdInterface.(string) + } + + subscriptionIdInterface, ok := d.GetOk("subscription_id") + if ok { + subscriptionId = subscriptionIdInterface.(string) + } + + clientSecretInterface, ok := d.GetOk("client_secret") + if ok { + clientSecret = clientSecretInterface.(string) + } + + tenantedDeploymentParticipationInterface, ok := d.GetOk("tenanted_deployment_participation") + if ok { + tenantedDeploymentParticipation = tenantedDeploymentParticipationInterface.(string) + } + + tenantTagsInterface, ok := d.GetOk("tenant_tags") + if ok { + tenantTags = getSliceFromTerraformTypeList(tenantTagsInterface) + } + + if tenantTags == nil { + tenantTags = []string{} + } + + var account = octopusdeploy.NewAccount(accountName, accountType) + account.ClientId = clientId + account.TenantId = tenantId + account.Password = octopusdeploy.SensitiveValue{ + NewValue: clientSecret, + } + account.SubscriptionNumber = subscriptionId + account.TenantTags = tenantTags + account.TenantedDeploymentParticipation = tenantedDeploymentParticipation + + return account +} + +func resourceAccountCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + newAccount := buildAccountResource(d) + account, err := client.Account.Add(newAccount) + + if err != nil { + return fmt.Errorf("error creating account %s: %s", newAccount.Name, err.Error()) + } + + d.SetId(account.ID) + + return nil +} + +func resourceAccountUpdate(d *schema.ResourceData, m interface{}) error { + account := buildAccountResource(d) + account.ID = d.Id() // set project struct ID so octopus knows which project to update + + client := m.(*octopusdeploy.Client) + + updatedAccount, err := client.Account.Update(account) + + if err != nil { + return fmt.Errorf("error updating account id %s: %s", d.Id(), err.Error()) + } + + d.SetId(updatedAccount.ID) + return nil +} + +func resourceAccountDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + accountId := d.Id() + + err := client.Account.Delete(accountId) + + if err != nil { + return fmt.Errorf("error deleting account id %s: %s", accountId, err.Error()) + } + + d.SetId("") + return nil +} diff --git a/octopusdeploy/resource_account_test.go b/octopusdeploy/resource_account_test.go new file mode 100644 index 000000000..b1a07c9bc --- /dev/null +++ b/octopusdeploy/resource_account_test.go @@ -0,0 +1,118 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployAccountBasic(t *testing.T) { + const accountPrefix = "octopusdeploy_account.foo" + const accountName = "Testing one two three" + const accountType = "AzureServicePrincipal" + const clientId = "18eb006b-c3c8-4a72-93cd-fe4b293f82e1" + const tenantId = "18eb006b-c3c8-4a72-93cd-fe4b293f82e2" + const subscriptionId = "18eb006b-c3c8-4a72-93cd-fe4b293f82e3" + const clientSecret = "18eb006b-c3c8-4a72-93cd-fe4b293f82e4" + const tagSetName = "TagSet" + const tagName = "Tag" + var tenantTags = fmt.Sprintf("%s/%s", tagSetName, tagName) + const tenantedDeploymentParticipation = "TenantedOrUntenanted" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testOctopusDeployAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccountBasic(tagSetName, tagName, accountName, accountType, clientId, tenantId, subscriptionId, clientSecret, tenantedDeploymentParticipation), + Check: resource.ComposeTestCheckFunc( + testOctopusDeployAccountExists(accountPrefix), + resource.TestCheckResourceAttr( + accountPrefix, "name", accountName), + resource.TestCheckResourceAttr( + accountPrefix, "account_type", accountType), + resource.TestCheckResourceAttr( + accountPrefix, "client_id", clientId), + resource.TestCheckResourceAttr( + accountPrefix, "tenant_id", tenantId), + resource.TestCheckResourceAttr( + accountPrefix, "subscription_id", subscriptionId), + resource.TestCheckResourceAttr( + accountPrefix, "client_secret", clientSecret), + resource.TestCheckResourceAttr( + accountPrefix, "tenant_tags.0", tenantTags), + resource.TestCheckResourceAttr( + accountPrefix, "tenanted_deployment_participation", tenantedDeploymentParticipation), + ), + }, + }, + }) +} + +func testAccountBasic(tagSetName string, tagName string, accountName string, accountType, clientId string, tenantId string, subscriptionId string, clientSecret string, tenantedDeploymentParticipation string) string { + return fmt.Sprintf(` + + resource "octopusdeploy_tag_set" "testtagset" { + name = "%s" + + tag { + name = "%s" + color = "#6e6e6f" + } + } + + + resource "octopusdeploy_account" "foo" { + name = "%s" + account_type = "%s" + client_id = "%s" + tenant_id = "%s" + subscription_id = "%s" + client_secret = "%s" + tenant_tags = ["${octopusdeploy_tag_set.testtagset.name}/%s"] + tenanted_deployment_participation = "%s" + } + `, + tagSetName, tagName, accountName, accountType, clientId, tenantId, subscriptionId, clientSecret, tagName, tenantedDeploymentParticipation, + ) +} + +func testOctopusDeployAccountExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + return existsaccountHelper(s, client) + } +} + +func existsaccountHelper(s *terraform.State, client *octopusdeploy.Client) error { + + accountId := s.RootModule().Resources["octopusdeploy_account.foo"].Primary.ID + + if _, err := client.Account.Get(accountId); err != nil { + return fmt.Errorf("Received an error retrieving account %s", err) + } + + return nil +} + +func testOctopusDeployAccountDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + return destroyaccountHelper(s, client) +} + +func destroyaccountHelper(s *terraform.State, client *octopusdeploy.Client) error { + + accountId := s.RootModule().Resources["octopusdeploy_account.foo"].Primary.ID + + if _, err := client.Account.Get(accountId); err != nil { + if err == octopusdeploy.ErrItemNotFound { + return nil + } + return fmt.Errorf("Received an error retrieving account %s", err) + } + return fmt.Errorf("Account still exists") +} diff --git a/octopusdeploy/resource_environment.go b/octopusdeploy/resource_environment.go index 5d2871aba..ee4a47404 100644 --- a/octopusdeploy/resource_environment.go +++ b/octopusdeploy/resource_environment.go @@ -31,6 +31,11 @@ func resourceEnvironment() *schema.Resource { Optional: true, Default: false, }, + "allow_dynamic_infrastructure": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, }, } } @@ -53,6 +58,7 @@ func resourceEnvironmentRead(d *schema.ResourceData, m interface{}) error { d.Set("name", env.Name) d.Set("description", env.Description) d.Set("use_guided_failure", env.UseGuidedFailure) + d.Set("allow_dynamic_infrastructure", env.AllowDynamicInfrastructure) return nil } @@ -62,6 +68,7 @@ func buildEnvironmentResource(d *schema.ResourceData) *octopusdeploy.Environment var envDesc string var envGuided bool + var envDynamic bool envDescInterface, ok := d.GetOk("description") if ok { @@ -73,7 +80,15 @@ func buildEnvironmentResource(d *schema.ResourceData) *octopusdeploy.Environment envGuided = envGuidedInterface.(bool) } - return octopusdeploy.NewEnvironment(envName, envDesc, envGuided) + allowDynamicInfrastructureInterface, ok := d.GetOk("allow_dynamic_infrastructure") + if ok { + envDynamic = allowDynamicInfrastructureInterface.(bool) + } + + var environment = octopusdeploy.NewEnvironment(envName, envDesc, envGuided) + environment.AllowDynamicInfrastructure = envDynamic + + return environment } func resourceEnvironmentCreate(d *schema.ResourceData, m interface{}) error { diff --git a/octopusdeploy/resource_environment_test.go b/octopusdeploy/resource_environment_test.go index 37b1674f7..72732e35b 100644 --- a/octopusdeploy/resource_environment_test.go +++ b/octopusdeploy/resource_environment_test.go @@ -14,13 +14,15 @@ func TestAccOctopusDeployEnvironmentBasic(t *testing.T) { const envName = "Testing one two three" const envDesc = "Terraform testing module environment" const envGuided = "false" + const envDynamic = "false" + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testOctopusDeployEnvironmentDestroy, Steps: []resource.TestStep{ { - Config: testEnvironmenttBasic(envName, envDesc, envGuided), + Config: testEnvironmenttBasic(envName, envDesc, envGuided, envDynamic), Check: resource.ComposeTestCheckFunc( testOctopusDeployEnvironmentExists(envPrefix), resource.TestCheckResourceAttr( @@ -29,21 +31,24 @@ func TestAccOctopusDeployEnvironmentBasic(t *testing.T) { envPrefix, "description", envDesc), resource.TestCheckResourceAttr( envPrefix, "use_guided_failure", envGuided), + resource.TestCheckResourceAttr( + envPrefix, "allow_dynamic_infrastructure", envDynamic), ), }, }, }) } -func testEnvironmenttBasic(name, description, useguided string) string { +func testEnvironmenttBasic(name, description, useguided string, dynamic string) string { return fmt.Sprintf(` resource "octopusdeploy_environment" "foo" { name = "%s" description = "%s" use_guided_failure = "%s" + allow_dynamic_infrastructure = "%s" } `, - name, description, useguided, + name, description, useguided, dynamic, ) } diff --git a/octopusdeploy/resource_feed.go b/octopusdeploy/resource_feed.go new file mode 100644 index 000000000..11d3adad5 --- /dev/null +++ b/octopusdeploy/resource_feed.go @@ -0,0 +1,186 @@ +package octopusdeploy + +import ( + "fmt" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceFeed() *schema.Resource { + return &schema.Resource{ + Create: resourceFeedCreate, + Read: resourceFeedRead, + Update: resourceFeedUpdate, + Delete: resourceFeedDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "feed_type": { + Type: schema.TypeString, + Required: true, + }, + "feed_uri": { + Type: schema.TypeString, + Required: true, + }, + "enhanced_mode": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "download_attempts": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + }, + "download_retry_backoff_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 10, + }, + "username": { + Type: schema.TypeString, + Optional: true, + }, + "password": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceFeedRead(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + feedId := d.Id() + feed, err := client.Feed.Get(feedId) + + if err == octopusdeploy.ErrItemNotFound { + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading feed %s: %s", feedId, err.Error()) + } + + d.Set("name", feed.Name) + d.Set("feed_type", feed.FeedType) + d.Set("feed_uri", feed.FeedUri) + d.Set("enhanced_mode", feed.EnhancedMode) + d.Set("download_attempts", feed.DownloadAttempts) + d.Set("download_retry_backoff_seconds", feed.DownloadRetryBackoffSeconds) + d.Set("username", feed.Username) + d.Set("password", feed.Password) + + return nil +} + +func buildFeedResource(d *schema.ResourceData) *octopusdeploy.Feed { + feedName := d.Get("name").(string) + + var feedType string + var feedUri string + var enhancedMode bool + var downloadAttempts int + var downloadRetryBackoffSeconds int + var feedUsername string + var feedPassword string + + feedTypeInterface, ok := d.GetOk("feed_type") + if ok { + feedType = feedTypeInterface.(string) + } + + feedUriInterface, ok := d.GetOk("feed_uri") + if ok { + feedUri = feedUriInterface.(string) + } + + enhancedModeInterface, ok := d.GetOk("enhanced_mode") + if ok { + enhancedMode = enhancedModeInterface.(bool) + } + + downloadAttemptsInterface, ok := d.GetOk("download_attempts") + if ok { + downloadAttempts = downloadAttemptsInterface.(int) + } + + downloadRetryBackoffSecondsInterface, ok := d.GetOk("download_retry_backoff_seconds") + if ok { + downloadRetryBackoffSeconds = downloadRetryBackoffSecondsInterface.(int) + } + + feedUsernameInterface, ok := d.GetOk("username") + if ok { + feedUsername = feedUsernameInterface.(string) + } + + feedPasswordInterface, ok := d.GetOk("password") + if ok { + feedPassword = feedPasswordInterface.(string) + } + + var feed = octopusdeploy.NewFeed(feedName, feedType, feedUri) + feed.EnhancedMode = enhancedMode + feed.DownloadAttempts = downloadAttempts + feed.DownloadRetryBackoffSeconds = downloadRetryBackoffSeconds + feed.Username = feedUsername + feed.Password = octopusdeploy.SensitiveValue{ + NewValue: feedPassword, + } + + return feed +} + +func resourceFeedCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + newFeed := buildFeedResource(d) + feed, err := client.Feed.Add(newFeed) + + if err != nil { + return fmt.Errorf("error creating feed %s: %s", newFeed.Name, err.Error()) + } + + d.SetId(feed.ID) + + return nil +} + +func resourceFeedUpdate(d *schema.ResourceData, m interface{}) error { + feed := buildFeedResource(d) + feed.ID = d.Id() // set project struct ID so octopus knows which project to update + + client := m.(*octopusdeploy.Client) + + updatedFeed, err := client.Feed.Update(feed) + + if err != nil { + return fmt.Errorf("error updating feed id %s: %s", d.Id(), err.Error()) + } + + d.SetId(updatedFeed.ID) + return nil +} + +func resourceFeedDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + feedId := d.Id() + + err := client.Feed.Delete(feedId) + + if err != nil { + return fmt.Errorf("error deleting feed id %s: %s", feedId, err.Error()) + } + + d.SetId("") + return nil +} diff --git a/octopusdeploy/resource_feed_test.go b/octopusdeploy/resource_feed_test.go new file mode 100644 index 000000000..e52327e6c --- /dev/null +++ b/octopusdeploy/resource_feed_test.go @@ -0,0 +1,95 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployFeedBasic(t *testing.T) { + const feedPrefix = "octopusdeploy_feed.foo" + const feedName = "Testing one two three" + const feedType = "NuGet" + const feedUri = "http://test.com" + const enhancedMode = "true" + const feedUsername = "username" + const feedPassword = "password" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testOctopusDeployFeedDestroy, + Steps: []resource.TestStep{ + { + Config: testFeedtBasic(feedName, feedType, feedUri, feedUsername, feedPassword, enhancedMode), + Check: resource.ComposeTestCheckFunc( + testOctopusDeployFeedExists(feedPrefix), + resource.TestCheckResourceAttr( + feedPrefix, "name", feedName), + resource.TestCheckResourceAttr( + feedPrefix, "feed_type", feedType), + resource.TestCheckResourceAttr( + feedPrefix, "feed_uri", feedUri), + resource.TestCheckResourceAttr( + feedPrefix, "username", feedUsername), + resource.TestCheckResourceAttr( + feedPrefix, "password", feedPassword), + resource.TestCheckResourceAttr( + feedPrefix, "enhanced_mode", enhancedMode), + ), + }, + }, + }) +} + +func testFeedtBasic(name, feedType, feedUri string, feedUsername string, feedPassword string, enhancedMode string) string { + return fmt.Sprintf(` + resource "octopusdeploy_feed" "foo" { + name = "%s" + feed_type = "%s" + feed_uri = "%s" + username = "%s" + password = "%s" + enhanced_mode = "%s" + } + `, + name, feedType, feedUri, feedUsername, feedPassword, enhancedMode, + ) +} + +func testOctopusDeployFeedExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + return existsfeedHelper(s, client) + } +} + +func existsfeedHelper(s *terraform.State, client *octopusdeploy.Client) error { + for _, r := range s.RootModule().Resources { + if _, err := client.Feed.Get(r.Primary.ID); err != nil { + return fmt.Errorf("Received an error retrieving feed %s", err) + } + } + return nil +} + +func testOctopusDeployFeedDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + return destroyfeedHelper(s, client) +} + +func destroyfeedHelper(s *terraform.State, client *octopusdeploy.Client) error { + for _, r := range s.RootModule().Resources { + if _, err := client.Feed.Get(r.Primary.ID); err != nil { + if err == octopusdeploy.ErrItemNotFound { + continue + } + return fmt.Errorf("Received an error retrieving feed %s", err) + } + return fmt.Errorf("Feed still exists") + } + return nil +} diff --git a/octopusdeploy/resource_library_variable_set.go b/octopusdeploy/resource_library_variable_set.go index e6d0a410c..1d4981cfd 100644 --- a/octopusdeploy/resource_library_variable_set.go +++ b/octopusdeploy/resource_library_variable_set.go @@ -23,6 +23,22 @@ func resourceLibraryVariableSet() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "templates": getTemplatesSchema(), + }, + } +} + +func getTemplatesSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + }, }, } } @@ -52,9 +68,29 @@ func buildLibraryVariableSetResource(d *schema.ResourceData) *octopusdeploy.Libr libraryVariableSet.Description = attr.(string) } + if attr, ok := d.GetOk("templates"); ok { + tfTemplates := attr.([]interface{}) + + for _, tfTemplate := range tfTemplates { + template := buildTemplateResource(tfTemplate.(map[string]interface{})) + libraryVariableSet.Templates = append(libraryVariableSet.Templates, template) + } + } + return libraryVariableSet } +func buildTemplateResource(tfTemplate map[string]interface{}) octopusdeploy.ActionTemplateParameter { + template := octopusdeploy.ActionTemplateParameter{ + Name: tfTemplate["name"].(string), + DisplaySettings: map[string]string{ + "Octopus.ControlType": "SingleLineText", + }, + } + + return template +} + func resourceLibraryVariableSetRead(d *schema.ResourceData, m interface{}) error { client := m.(*octopusdeploy.Client) diff --git a/octopusdeploy/resource_project.go b/octopusdeploy/resource_project.go index 8b1f84757..27609a845 100644 --- a/octopusdeploy/resource_project.go +++ b/octopusdeploy/resource_project.go @@ -57,6 +57,11 @@ func resourceProject() *schema.Resource { "None", }), }, + "allow_deployments_to_no_targets": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "tenanted_deployment_mode": { Type: schema.TypeString, Optional: true, @@ -89,6 +94,7 @@ func resourceProject() *schema.Resource { "deployment_step_iis_website": getDeploymentStepIISWebsiteSchema(), "deployment_step_inline_script": getDeploymentStepInlineScriptSchema(), "deployment_step_package_script": getDeploymentStepPackageScriptSchema(), + "deployment_step_apply_terraform": getDeploymentStepApplyTerraformSchema(), }, } } @@ -137,6 +143,12 @@ func addConfigurationTransformDeploymentStepSchema(schemaToAddToo interface{}) * Description: "A comma-separated list of file names to replace settings in, relative to the package contents.", } + schemaResource.Schema["variable_substitution_in_files"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "A newline-separated list of file names to transform, relative to the package contents. Extended wildcard syntax is supported.", + } + return schemaResource } @@ -285,6 +297,41 @@ func getDeploymentStepPackageScriptSchema() *schema.Schema { }, } + schemaToReturn.Elem = addConfigurationTransformDeploymentStepSchema(schemaToReturn.Elem) + schemaToReturn.Elem = addFeedAndPackageDeploymentStepSchema(schemaToReturn.Elem) + schemaToReturn.Elem = addStandardDeploymentStepSchema(schemaToReturn.Elem, false) + + return schemaToReturn +} + +func getDeploymentStepApplyTerraformSchema() *schema.Schema { + schemaToReturn := &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "additional_init_params": { + Type: schema.TypeString, + Description: "Additional parameters passed to the init command.", + Optional: true, + }, + "run_on_server": { + Type: schema.TypeBool, + Description: "Whether the script runs on the server (true) or target (false)", + Optional: true, + Default: false, + }, + "terraform_file_variable_replacement": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + } + schemaToReturn.Elem = addFeedAndPackageDeploymentStepSchema(schemaToReturn.Elem) schemaToReturn.Elem = addStandardDeploymentStepSchema(schemaToReturn.Elem, false) @@ -389,6 +436,7 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde executablePath := localStep["executable_path"].(string) feedID := localStep["feed_id"].(string) jsonFileVariableReplacement := localStep["json_file_variable_replacement"].(string) + variableSubstitutionInFiles := localStep["variable_substitution_in_files"].(string) packageID := localStep["package"].(string) serviceAccount := localStep["service_account"].(string) serviceName := localStep["service_name"].(string) @@ -430,6 +478,13 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.JsonConfigurationVariables" } + if variableSubstitutionInFiles != "" { + deploymentStep.Actions[0].Properties["Octopus.Action.SubstituteInFiles.TargetFiles"] = variableSubstitutionInFiles + deploymentStep.Actions[0].Properties["Octopus.Action.SubstituteInFiles.Enabled"] = "True" + + deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.SubstituteInFiles" + } + if targetRolesInterface, ok := localStep["target_roles"]; ok { var targetRoleSlice []string @@ -461,6 +516,7 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde configurationVariables := localStep["configuration_variables"].(bool) feedID := localStep["feed_id"].(string) jsonFileVariableReplacement := localStep["json_file_variable_replacement"].(string) + variableSubstitutionInFiles := localStep["variable_substitution_in_files"].(string) packageID := localStep["package"].(string) stepCondition := localStep["step_condition"].(string) stepName := localStep["step_name"].(string) @@ -511,6 +567,13 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.JsonConfigurationVariables" } + if variableSubstitutionInFiles != "" { + deploymentStep.Actions[0].Properties["Octopus.Action.SubstituteInFiles.TargetFiles"] = variableSubstitutionInFiles + deploymentStep.Actions[0].Properties["Octopus.Action.SubstituteInFiles.Enabled"] = "True" + + deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.SubstituteInFiles" + } + if targetRolesInterface, ok := localStep["target_roles"]; ok { var targetRoleSlice []string @@ -586,6 +649,10 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde scriptParameters := localStep["script_parameters"].(string) feedID := localStep["feed_id"].(string) packageID := localStep["package"].(string) + jsonFileVariableReplacement := localStep["json_file_variable_replacement"].(string) + variableSubstitutionInFiles := localStep["variable_substitution_in_files"].(string) + configurationTransforms := localStep["configuration_transforms"].(bool) + configurationVariables := localStep["configuration_variables"].(bool) stepCondition := localStep["step_condition"].(string) stepName := localStep["step_name"].(string) stepStartTrigger := localStep["step_start_trigger"].(string) @@ -613,6 +680,85 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde }, } + if jsonFileVariableReplacement != "" { + deploymentStep.Actions[0].Properties["Octopus.Action.Package.JsonConfigurationVariablesTargets"] = jsonFileVariableReplacement + deploymentStep.Actions[0].Properties["Octopus.Action.Package.JsonConfigurationVariablesEnabled"] = "True" + + deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.JsonConfigurationVariables" + } + + if variableSubstitutionInFiles != "" { + deploymentStep.Actions[0].Properties["Octopus.Action.SubstituteInFiles.TargetFiles"] = variableSubstitutionInFiles + deploymentStep.Actions[0].Properties["Octopus.Action.SubstituteInFiles.Enabled"] = "True" + + deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.SubstituteInFiles" + } + + if configurationTransforms { + deploymentStep.Actions[0].Properties["Octopus.Action.Package.AutomaticallyRunConfigurationTransformationFiles"] = strconv.FormatBool(configurationTransforms) + deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.ConfigurationTransforms" + } + + if configurationVariables { + deploymentStep.Actions[0].Properties["Octopus.Action.Package.AutomaticallyUpdateAppSettingsAndConnectionStrings"] = strconv.FormatBool(configurationVariables) + deploymentStep.Actions[0].Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.ConfigurationVariables" + } + + if targetRolesInterface, ok := localStep["target_roles"]; ok { + var targetRoleSlice []string + + targetRoles := targetRolesInterface.([]interface{}) + + for _, role := range targetRoles { + targetRoleSlice = append(targetRoleSlice, role.(string)) + } + + deploymentStep.Properties = map[string]string{"Octopus.Action.TargetRoles": strings.Join(targetRoleSlice, ",")} + } + + deploymentProcess.Steps = append(deploymentProcess.Steps, *deploymentStep) + } + } + + if v, ok := d.GetOk("deployment_step_apply_terraform"); ok { + steps := v.([]interface{}) + for _, raw := range steps { + + localStep := raw.(map[string]interface{}) + + feedID := localStep["feed_id"].(string) + packageID := localStep["package"].(string) + stepCondition := localStep["step_condition"].(string) + stepName := localStep["step_name"].(string) + stepStartTrigger := localStep["step_start_trigger"].(string) + runOnServer := localStep["run_on_server"].(bool) + additionalInitParams := localStep["additional_init_params"].(string) + + deploymentStep := &octopusdeploy.DeploymentStep{ + Name: stepName, + PackageRequirement: "LetOctopusDecide", + Condition: octopusdeploy.DeploymentStepCondition(stepCondition), + StartTrigger: octopusdeploy.DeploymentStepStartTrigger(stepStartTrigger), + Actions: []octopusdeploy.DeploymentAction{ + { + Name: stepName, + ActionType: "Octopus.TerraformApply", + Properties: map[string]string{ + "Octopus.Action.RunOnServer": strconv.FormatBool(runOnServer), + "Octopus.Action.Script.ScriptSource": "Package", + "Octopus.Action.Package.DownloadOnTentacle": "False", + "Octopus.Action.Package.FeedId": feedID, + "Octopus.Action.Package.PackageId": packageID, + "Octopus.Action.Aws.AssumeRole": "False", + "Octopus.Action.AwsAccount.UseInstanceRole": "False", + "Octopus.Action.Terraform.AdditionalInitParams": additionalInitParams, + "Octopus.Action.Terraform.AllowPluginDownloads": "True", + "Octopus.Action.Terraform.ManagedAccount": "None", + }, + }, + }, + } + if targetRolesInterface, ok := localStep["target_roles"]; ok { var targetRoleSlice []string @@ -625,6 +771,18 @@ func buildDeploymentProcess(d *schema.ResourceData, deploymentProcess *octopusde deploymentStep.Properties = map[string]string{"Octopus.Action.TargetRoles": strings.Join(targetRoleSlice, ",")} } + if targetFilesInterface, ok := localStep["terraform_file_variable_replacement"]; ok { + var targetFilesSlice []string + + targetFiles := targetFilesInterface.([]interface{}) + + for _, file := range targetFiles { + targetFilesSlice = append(targetFilesSlice, file.(string)) + } + + deploymentStep.Properties = map[string]string{"Octopus.Action.Terraform.FileSubstitution": strings.Join(targetFilesSlice, "\n")} + } + deploymentProcess.Steps = append(deploymentProcess.Steps, *deploymentStep) } } @@ -651,6 +809,10 @@ func buildProjectResource(d *schema.ResourceData) *octopusdeploy.Project { project.ProjectConnectivityPolicy.SkipMachineBehavior = attr.(string) } + if attr, ok := d.GetOk("allow_deployments_to_no_targets"); ok { + project.ProjectConnectivityPolicy.AllowDeploymentsToNoTargets = attr.(bool) + } + if attr, ok := d.GetOk("tenanted_deployment_mode"); ok { project.TenantedDeploymentMode = attr.(string) } @@ -739,6 +901,7 @@ func resourceProjectRead(d *schema.ResourceData, m interface{}) error { d.Set("project_group_id", project.ProjectGroupID) d.Set("default_failure_mode", project.DefaultGuidedFailureMode) d.Set("skip_machine_behavior", project.ProjectConnectivityPolicy.SkipMachineBehavior) + d.Set("allow_deployments_to_no_targets", project.ProjectConnectivityPolicy.AllowDeploymentsToNoTargets) return nil } diff --git a/octopusdeploy/resource_project_test.go b/octopusdeploy/resource_project_test.go index f4ddf34c9..6ef08c71b 100644 --- a/octopusdeploy/resource_project_test.go +++ b/octopusdeploy/resource_project_test.go @@ -15,21 +15,22 @@ func TestAccOctopusDeployProjectBasic(t *testing.T) { const terraformNamePrefix = "octopusdeploy_project.foo" const projectName = "Funky Monkey" const lifeCycleID = "Lifecycles-1" + const allowDeploymentsToNoTargets = "true" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckOctopusDeployProjectDestroy, Steps: []resource.TestStep{ { - Config: testAccProjectBasic(projectName, lifeCycleID), + Config: testAccProjectBasic(projectName, lifeCycleID, allowDeploymentsToNoTargets), Check: resource.ComposeTestCheckFunc( testAccCheckOctopusDeployProjectExists(terraformNamePrefix), resource.TestCheckResourceAttr( terraformNamePrefix, "name", projectName), resource.TestCheckResourceAttr( terraformNamePrefix, "lifecycle_id", lifeCycleID), - resource.TestCheckResourceAttrSet( - terraformNamePrefix, "project_group_id"), + resource.TestCheckResourceAttr( + terraformNamePrefix, "allow_deployments_to_no_targets", allowDeploymentsToNoTargets), ), }, }, @@ -41,7 +42,7 @@ func TestAccOctopusDeployProjectWithDeploymentStepWindowsService(t *testing.T) { const projectName = "Funky Monkey" const lifeCycleID = "Lifecycles-1" const serviceName = "Epic Service" - const executablePath = `bin\\MyService.exe` + const executablePath = `bin\\MyService.exe` // needs 4 slashes to appear in the TF config as a double slash const stepName = "Deploying Epic Service" const packageName = "MyPackage" targetRoles := []string{"Lab1", "Lab2"} @@ -63,8 +64,6 @@ func TestAccOctopusDeployProjectWithDeploymentStepWindowsService(t *testing.T) { terraformNamePrefix, "name", projectName), resource.TestCheckResourceAttr( terraformNamePrefix, "lifecycle_id", lifeCycleID), - resource.TestCheckResourceAttrSet( - terraformNamePrefix, "project_group_id"), resource.TestCheckResourceAttr( terraformNamePrefix, "deployment_step_windows_service.0.service_name", serviceName), resource.TestCheckResourceAttr( @@ -87,7 +86,8 @@ func TestAccOctopusDeployProjectWithUpdate(t *testing.T) { const terraformNamePrefix = "octopusdeploy_project.foo" const projectName = "Funky Monkey" const lifeCycleID = "Lifecycles-1" - inlineScriptRegex := regexp.MustCompile(`.*Get\-Process.*`) + const allowDeploymentsToNoTargets = "true" + inlineScriptRegex := regexp.MustCompile(".*Get\\-Process.*") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -95,15 +95,13 @@ func TestAccOctopusDeployProjectWithUpdate(t *testing.T) { Steps: []resource.TestStep{ // create project with no description { - Config: testAccProjectBasic(projectName, lifeCycleID), + Config: testAccProjectBasic(projectName, lifeCycleID, allowDeploymentsToNoTargets), Check: resource.ComposeTestCheckFunc( testAccCheckOctopusDeployProjectExists(terraformNamePrefix), resource.TestCheckResourceAttr( terraformNamePrefix, "name", projectName), resource.TestCheckResourceAttr( terraformNamePrefix, "lifecycle_id", lifeCycleID), - resource.TestCheckResourceAttrSet( - terraformNamePrefix, "project_group_id"), ), }, // create update it with a description + build steps @@ -115,8 +113,6 @@ func TestAccOctopusDeployProjectWithUpdate(t *testing.T) { terraformNamePrefix, "name", "Project Name"), resource.TestCheckResourceAttr( terraformNamePrefix, "lifecycle_id", "Lifecycles-1"), - resource.TestCheckResourceAttrSet( - terraformNamePrefix, "project_group_id"), resource.TestCheckResourceAttr( terraformNamePrefix, "description", "My Awesome Description"), resource.TestCheckResourceAttr( @@ -193,15 +189,13 @@ func TestAccOctopusDeployProjectWithUpdate(t *testing.T) { }, // update again by remove its description { - Config: testAccProjectBasic(projectName, lifeCycleID), + Config: testAccProjectBasic(projectName, lifeCycleID, allowDeploymentsToNoTargets), Check: resource.ComposeTestCheckFunc( testAccCheckOctopusDeployProjectExists(terraformNamePrefix), resource.TestCheckResourceAttr( terraformNamePrefix, "name", projectName), resource.TestCheckResourceAttr( terraformNamePrefix, "lifecycle_id", lifeCycleID), - resource.TestCheckResourceAttrSet( - terraformNamePrefix, "project_group_id"), resource.TestCheckResourceAttr( terraformNamePrefix, "description", ""), resource.TestCheckNoResourceAttr( @@ -216,7 +210,7 @@ func TestAccOctopusDeployProjectWithUpdate(t *testing.T) { }) } -func testAccProjectBasic(name, lifeCycleID string) string { +func testAccProjectBasic(name, lifeCycleID, allowDeploymentsToNoTargets string) string { return fmt.Sprintf(` resource "octopusdeploy_project_group" "foo" { name = "Integration Test Project Group" @@ -226,9 +220,10 @@ func testAccProjectBasic(name, lifeCycleID string) string { name = "%s" lifecycle_id = "%s" project_group_id = "${octopusdeploy_project_group.foo.id}" + allow_deployments_to_no_targets = "%s" } `, - name, lifeCycleID, + name, lifeCycleID, allowDeploymentsToNoTargets, ) } @@ -320,6 +315,7 @@ resource "octopusdeploy_project" "foo" { func testAccWithDeploymentStepWindowsService(name, lifeCycleID, serviceName, executablePath, stepName, packageName string, targetRoles []string) string { return fmt.Sprintf(` + resource "octopusdeploy_project_group" "foo" { name = "Integration Test Project Group" } @@ -357,7 +353,10 @@ func testAccCheckOctopusDeployProjectDestroy(s *terraform.State) error { func testAccCheckOctopusDeployProjectExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { client := testAccProvider.Meta().(*octopusdeploy.Client) - return existsHelper(s, client) + if err := existsHelper(s, client); err != nil { + return err + } + return nil } } diff --git a/octopusdeploy/resource_tag_set.go b/octopusdeploy/resource_tag_set.go new file mode 100644 index 000000000..aa4191a9c --- /dev/null +++ b/octopusdeploy/resource_tag_set.go @@ -0,0 +1,137 @@ +package octopusdeploy + +import ( + "fmt" + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceTagSet() *schema.Resource { + return &schema.Resource{ + Create: resourceTagSetCreate, + Read: resourceTagSetRead, + Update: resourceTagSetUpdate, + Delete: resourceTagSetDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "tag": getTagSchema(), + }, + } +} + +func getTagSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "The name of the step", + Required: true, + }, + "color": { + Type: schema.TypeString, + Description: "The name of the step", + Required: true, + }, + }, + }, + } +} + +func resourceTagSetRead(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + tagSetId := d.Id() + tagSet, err := client.TagSet.Get(tagSetId) + + if err == octopusdeploy.ErrItemNotFound { + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading tagSet %s: %s", tagSetId, err.Error()) + } + + d.Set("name", tagSet.Name) + + return nil +} + +func buildTagSetResource(d *schema.ResourceData) *octopusdeploy.TagSet { + tagSetName := d.Get("name").(string) + + var tagSet = octopusdeploy.NewTagSet(tagSetName) + + if attr, ok := d.GetOk("tag"); ok { + tfTags := attr.([]interface{}) + + for _, tfTag := range tfTags { + tag := buildTagResource(tfTag.(map[string]interface{})) + tagSet.Tags = append(tagSet.Tags, tag) + } + } + + return tagSet +} + +func buildTagResource(tfTag map[string]interface{}) octopusdeploy.Tag { + tag := octopusdeploy.Tag{ + Name: tfTag["name"].(string), + Color: tfTag["color"].(string), + } + + return tag +} + +func resourceTagSetCreate(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + newTagSet := buildTagSetResource(d) + tagSet, err := client.TagSet.Add(newTagSet) + + if err != nil { + return fmt.Errorf("error creating tagSet %s: %s", newTagSet.Name, err.Error()) + } + + d.SetId(tagSet.ID) + + return nil +} + +func resourceTagSetUpdate(d *schema.ResourceData, m interface{}) error { + tagSet := buildTagSetResource(d) + tagSet.ID = d.Id() // set project struct ID so octopus knows which project to update + + client := m.(*octopusdeploy.Client) + + updatedTagSet, err := client.TagSet.Update(tagSet) + + if err != nil { + return fmt.Errorf("error updating tagSet id %s: %s", d.Id(), err.Error()) + } + + d.SetId(updatedTagSet.ID) + return nil +} + +func resourceTagSetDelete(d *schema.ResourceData, m interface{}) error { + client := m.(*octopusdeploy.Client) + + tagSetId := d.Id() + + err := client.TagSet.Delete(tagSetId) + + if err != nil { + return fmt.Errorf("error deleting tagSet id %s: %s", tagSetId, err.Error()) + } + + d.SetId("") + return nil +} diff --git a/octopusdeploy/resource_tag_set_test.go b/octopusdeploy/resource_tag_set_test.go new file mode 100644 index 000000000..4bb5331b5 --- /dev/null +++ b/octopusdeploy/resource_tag_set_test.go @@ -0,0 +1,97 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployTagSetBasic(t *testing.T) { + const tagSetPrefix = "octopusdeploy_tag_set.foo" + const tagSetName = "Testing one two three" + const tagName1 = "tagName1" + const tagName2 = "tagName2" + const tagColor1 = "#6e6e6e" + const tagColor2 = "#6e6e6f" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testOctopusDeployTagSetDestroy, + Steps: []resource.TestStep{ + { + Config: testTagSettBasic(tagSetName, tagName1, tagColor1, tagName2, tagColor2), + Check: resource.ComposeTestCheckFunc( + testOctopusDeployTagSetExists(tagSetPrefix), + resource.TestCheckResourceAttr( + tagSetPrefix, "name", tagSetName), + resource.TestCheckResourceAttr( + tagSetPrefix, "tag.0.name", tagName1), + resource.TestCheckResourceAttr( + tagSetPrefix, "tag.0.color", tagColor1), + resource.TestCheckResourceAttr( + tagSetPrefix, "tag.1.name", tagName2), + resource.TestCheckResourceAttr( + tagSetPrefix, "tag.1.color", tagColor2), + ), + }, + }, + }) +} + +func testTagSettBasic(name, tagName1 string, tagColor1 string, tagName2 string, tagColor2 string) string { + return fmt.Sprintf(` + resource "octopusdeploy_tag_set" "foo" { + name = "%s" + + tag { + name = "%s" + color = "%s" + } + + tag { + name = "%s" + color = "%s" + } + } + `, + name, tagName1, tagColor1, tagName2, tagColor2, + ) +} + +func testOctopusDeployTagSetExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + return existstagSetHelper(s, client) + } +} + +func existstagSetHelper(s *terraform.State, client *octopusdeploy.Client) error { + for _, r := range s.RootModule().Resources { + if _, err := client.TagSet.Get(r.Primary.ID); err != nil { + return fmt.Errorf("Received an error retrieving tagSet %s", err) + } + } + return nil +} + +func testOctopusDeployTagSetDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + return destroytagSetHelper(s, client) +} + +func destroytagSetHelper(s *terraform.State, client *octopusdeploy.Client) error { + for _, r := range s.RootModule().Resources { + if _, err := client.TagSet.Get(r.Primary.ID); err != nil { + if err == octopusdeploy.ErrItemNotFound { + continue + } + return fmt.Errorf("Received an error retrieving tagSet %s", err) + } + return fmt.Errorf("TagSet still exists") + } + return nil +} diff --git a/octopusdeploy/resource_variable.go b/octopusdeploy/resource_variable.go index 3a46f0001..be40032bd 100644 --- a/octopusdeploy/resource_variable.go +++ b/octopusdeploy/resource_variable.go @@ -35,6 +35,7 @@ func resourceVariable() *schema.Resource { //"Sensitive", "Certificate", "AmazonWebServicesAccount", + "AzureAccount", }), }, "value": { diff --git a/octopusdeploy/run_kubectl_script_action.go b/octopusdeploy/run_kubectl_script_action.go new file mode 100644 index 000000000..00887c09b --- /dev/null +++ b/octopusdeploy/run_kubectl_script_action.go @@ -0,0 +1,48 @@ +package octopusdeploy + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func getRunRunKubectlScriptSchema() *schema.Schema { + + actionSchema, element := getCommonDeploymentActionSchema() + addExecutionLocationSchema(element) + addScriptFromPackageSchema(element) + addPackagesSchema(element, false) + + return actionSchema +} + +func buildRunKubectlScriptActionResource(tfAction map[string]interface{}) octopusdeploy.DeploymentAction { + resource := buildDeploymentActionResource(tfAction) + + resource.ActionType = "Octopus.KubernetesRunScript" + + resource.Properties = merge(resource.Properties, buildScriptFromPackageProperties(tfAction)) + + return resource +} +func merge(map1 map[string]string, map2 map[string]string) map[string]string { + result := make(map[string]string) + + for k, v := range map1 { + result[k] = v + } + + for k, v := range map2 { + result[k] = v + } + + return result +} +func buildScriptFromPackageProperties(tfAction map[string]interface{}) map[string]string { + + properties := make(map[string]string) + properties["Octopus.Action.Script.ScriptFileName"] = tfAction["script_file_name"].(string) + properties["Octopus.Action.Script.ScriptParameters"] = tfAction["script_parameters"].(string) + properties["Octopus.Action.Script.ScriptSource"] = "Package" + + return properties +} diff --git a/octopusdeploy/run_kubectl_script_action_test.go b/octopusdeploy/run_kubectl_script_action_test.go new file mode 100644 index 000000000..74049bb3e --- /dev/null +++ b/octopusdeploy/run_kubectl_script_action_test.go @@ -0,0 +1,70 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployRunKubectlScriptAction(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOctopusDeployDeploymentProcessDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRunKubectlScriptAction(), + Check: resource.ComposeTestCheckFunc( + testAccCheckRunKubectlScriptAction(), + ), + }, + }, + }) +} + +func testAccRunKubectlScriptAction() string { + return testAccBuildTestAction(` + run_kubectl_script_action { + name = "Run Script" + run_on_server = true + + primary_package { + package_id = "MyPackage" + feed_id = "feeds-builtin" + } + + script_file_name = "Test.ps1" + script_parameters = "-Test 1" + } + `) +} + +func testAccCheckRunKubectlScriptAction() resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + + process, err := getDeploymentProcess(s, client) + if err != nil { + return err + } + + action := process.Steps[0].Actions[0] + + if action.ActionType != "Octopus.KubernetesRunScript" { + return fmt.Errorf("Action type is incorrect: %s", action.ActionType) + } + + if action.Properties["Octopus.Action.Script.ScriptFileName"] != "Test.ps1" { + return fmt.Errorf("ScriptFileName is incorrect: %s", action.Properties["Octopus.Action.Script.ScriptFileName"]) + } + + if action.Properties["Octopus.Action.Script.ScriptParameters"] != "-Test 1" { + return fmt.Errorf("ScriptSource is incorrect: %s", action.Properties["Octopus.Action.Script.ScriptParameters"]) + } + + return nil + } +} diff --git a/octopusdeploy/run_script_action.go b/octopusdeploy/run_script_action.go new file mode 100644 index 000000000..a61ef9fbb --- /dev/null +++ b/octopusdeploy/run_script_action.go @@ -0,0 +1,67 @@ +package octopusdeploy + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/schema" +) + +func getRunScriptActionSchema() *schema.Schema { + + actionSchema, element := getCommonDeploymentActionSchema() + addExecutionLocationSchema(element) + addScriptFromPackageSchema(element) + addPackagesSchema(element, false) + + element.Schema["variable_substitution_in_files"] = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Description: "A newline-separated list of file names to transform, relative to the package contents. Extended wildcard syntax is supported.", + } + + return actionSchema +} + +func addScriptFromPackageSchema(element *schema.Resource) { + + element.Schema["script_file_name"] = &schema.Schema{ + Type: schema.TypeString, + Description: "The script file name in the package", + Optional: true, + } + + element.Schema["script_parameters"] = &schema.Schema{ + Type: schema.TypeString, + Description: "Parameters expected by the script. Use platform specific calling convention. e.g. -Path #{VariableStoringPath} for PowerShell or -- #{VariableStoringPath} for ScriptCS", + Optional: true, + } +} + +func buildRunScriptActionResource(tfAction map[string]interface{}) octopusdeploy.DeploymentAction { + resource := buildDeploymentActionResource(tfAction) + + resource.ActionType = "Octopus.Script" + + resource.Properties = merge(resource.Properties, buildRunScriptFromPackageActionResource(tfAction)) + + variableSubstitutionInFiles := tfAction["variable_substitution_in_files"].(string) + + if variableSubstitutionInFiles != "" { + resource.Properties["Octopus.Action.SubstituteInFiles.TargetFiles"] = variableSubstitutionInFiles + resource.Properties["Octopus.Action.SubstituteInFiles.Enabled"] = "True" + + resource.Properties["Octopus.Action.EnabledFeatures"] += ",Octopus.Features.SubstituteInFiles" + } + + return resource +} + +func buildRunScriptFromPackageActionResource(tfAction map[string]interface{}) map[string]string { + + properties := make(map[string]string) + + properties["Octopus.Action.Script.ScriptFileName"] = tfAction["script_file_name"].(string) + properties["Octopus.Action.Script.ScriptParameters"] = tfAction["script_parameters"].(string) + properties["Octopus.Action.Script.ScriptSource"] = "Package" + + return properties +} diff --git a/octopusdeploy/run_script_action_test.go b/octopusdeploy/run_script_action_test.go new file mode 100644 index 000000000..82220ee6c --- /dev/null +++ b/octopusdeploy/run_script_action_test.go @@ -0,0 +1,75 @@ +package octopusdeploy + +import ( + "fmt" + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccOctopusDeployRunScriptAction(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOctopusDeployDeploymentProcessDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRunScriptAction(), + Check: resource.ComposeTestCheckFunc( + testAccCheckRunScriptAction(), + ), + }, + }, + }) +} + +func testAccRunScriptAction() string { + return testAccBuildTestAction(` + run_script_action { + name = "Run Script" + run_on_server = true + + primary_package { + package_id = "MyPackage" + feed_id = "feeds-builtin" + } + + script_file_name = "Test.ps1" + script_parameters = "-Test 1" + variable_substitution_in_files = "test.json" + } + `) +} + +func testAccCheckRunScriptAction() resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*octopusdeploy.Client) + + process, err := getDeploymentProcess(s, client) + if err != nil { + return err + } + + action := process.Steps[0].Actions[0] + + if action.ActionType != "Octopus.Script" { + return fmt.Errorf("Action type is incorrect: %s", action.ActionType) + } + + if action.Properties["Octopus.Action.Script.ScriptFileName"] != "Test.ps1" { + return fmt.Errorf("ScriptFileName is incorrect: %s", action.Properties["Octopus.Action.Script.ScriptFileName"]) + } + + if action.Properties["Octopus.Action.Script.ScriptParameters"] != "-Test 1" { + return fmt.Errorf("ScriptSource is incorrect: %s", action.Properties["Octopus.Action.Script.ScriptParameters"]) + } + + if action.Properties["Octopus.Action.SubstituteInFiles.TargetFiles"] != "test.json" { + return fmt.Errorf("TargetFiles is incorrect: %s", action.Properties["Octopus.Action.SubstituteInFiles.TargetFiles"]) + } + + return nil + } +} diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/account.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/account.go new file mode 100644 index 000000000..bc8cbd743 --- /dev/null +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/account.go @@ -0,0 +1,140 @@ +package octopusdeploy + +import ( + "fmt" + + "github.com/dghubble/sling" + "gopkg.in/go-playground/validator.v9" +) + +type AccountService struct { + sling *sling.Sling +} + +func NewAccountService(sling *sling.Sling) *AccountService { + return &AccountService{ + sling: sling, + } +} + +type Accounts struct { + Items []Account `json:"Items"` + PagedResults +} + +type Account struct { + ID string `json:"Id"` + Name string `json:"Name"` + AccountType string `json:"AccountType"` + SubscriptionNumber string `json:"SubscriptionNumber"` + ClientId string `json:"ClientId"` + TenantId string `json:"TenantId"` + Password SensitiveValue `json:"Password"` + TenantTags []string `json:"TenantTags,omitempty"` + TenantedDeploymentParticipation string `json:"TenantedDeploymentParticipation"` +} + +func (t *Account) Validate() error { + validate := validator.New() + + err := validate.Struct(t) + + if err != nil { + return err + } + + return nil +} + +func NewAccount(name, accountType string) *Account { + return &Account{ + Name: name, + AccountType: accountType, + } +} + +func (s *AccountService) Get(accountId string) (*Account, error) { + path := fmt.Sprintf("accounts/%s", accountId) + resp, err := apiGet(s.sling, new(Account), path) + + if err != nil { + return nil, err + } + + return resp.(*Account), nil +} + +func (s *AccountService) GetAll() (*[]Account, error) { + var p []Account + + path := "accounts" + + loadNextPage := true + + for loadNextPage { + resp, err := apiGet(s.sling, new(Accounts), path) + + if err != nil { + return nil, err + } + + r := resp.(*Accounts) + + for _, item := range r.Items { + p = append(p, item) + } + + path, loadNextPage = LoadNextPage(r.PagedResults) + } + + return &p, nil +} + +func (s *AccountService) GetByName(accountName string) (*Account, error) { + var foundAccount Account + accounts, err := s.GetAll() + + if err != nil { + return nil, err + } + + for _, account := range *accounts { + if account.Name == accountName { + return &account, nil + } + } + + return &foundAccount, fmt.Errorf("no account found with account name %s", accountName) +} + +func (s *AccountService) Add(account *Account) (*Account, error) { + resp, err := apiAdd(s.sling, account, new(Account), "accounts") + + if err != nil { + return nil, err + } + + return resp.(*Account), nil +} + +func (s *AccountService) Delete(accountId string) error { + path := fmt.Sprintf("accounts/%s", accountId) + err := apiDelete(s.sling, path) + + if err != nil { + return err + } + + return nil +} + +func (s *AccountService) Update(account *Account) (*Account, error) { + path := fmt.Sprintf("accounts/%s", account.ID) + resp, err := apiUpdate(s.sling, account, new(Account), path) + + if err != nil { + return nil, err + } + + return resp.(*Account), nil +} diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/deployment_process.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/deployment_process.go index 7b5013960..c4aebfa3e 100644 --- a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/deployment_process.go +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/deployment_process.go @@ -59,21 +59,20 @@ type DeploymentAction struct { Packages []PackageReference `json:"Packages,omitempty"` } - type DeploymentStepPackageRequirement string const ( - DeploymentStepPackageRequirement_LetOctopusDecide = DeploymentStepPackageRequirement("LetOctopusDecide") + DeploymentStepPackageRequirement_LetOctopusDecide = DeploymentStepPackageRequirement("LetOctopusDecide") DeploymentStepPackageRequirement_BeforePackageAcquisition = DeploymentStepPackageRequirement("BeforePackageAcquisition") - DeploymentStepPackageRequirement_AfterPackageAcquisition = DeploymentStepPackageRequirement("AfterPackageAcquisition") + DeploymentStepPackageRequirement_AfterPackageAcquisition = DeploymentStepPackageRequirement("AfterPackageAcquisition") ) type DeploymentStepCondition string const ( - DeploymentStepCondition_Success = DeploymentStepCondition("Success") - DeploymentStepCondition_Failure = DeploymentStepCondition("Failure") - DeploymentStepCondition_Always = DeploymentStepCondition("Always") + DeploymentStepCondition_Success = DeploymentStepCondition("Success") + DeploymentStepCondition_Failure = DeploymentStepCondition("Failure") + DeploymentStepCondition_Always = DeploymentStepCondition("Always") DeploymentStepCondition_Variable = DeploymentStepCondition("Variable") ) @@ -81,7 +80,7 @@ type DeploymentStepStartTrigger string const ( DeploymentStepStartTrigger_StartAfterPrevious = DeploymentStepStartTrigger("StartAfterPrevious") - DeploymentStepStartTrigger_StartWithPrevious = DeploymentStepStartTrigger("StartWithPrevious") + DeploymentStepStartTrigger_StartWithPrevious = DeploymentStepStartTrigger("StartWithPrevious") ) type PackageReference struct { @@ -94,12 +93,11 @@ type PackageReference struct { } const ( - PackageAcquisitionLocation_Server = "Server" + PackageAcquisitionLocation_Server = "Server" PackageAcquisitionLocation_ExecutionTarget = "ExecutionTarget" - PackageAcquisitionLocation_NotAcquired = "NotAcquired" + PackageAcquisitionLocation_NotAcquired = "NotAcquired" ) - func (d *DeploymentProcess) Validate() error { validate := validator.New() diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/environment.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/environment.go index 4546794cb..edbd40a76 100644 --- a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/environment.go +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/environment.go @@ -23,11 +23,12 @@ type Environments struct { } type Environment struct { - ID string `json:"Id"` - Name string `json:"Name"` - Description string `json:"Description"` - SortOrder int `json:"SortOrder"` - UseGuidedFailure bool `json:"UseGuidedFailure"` + ID string `json:"Id"` + Name string `json:"Name"` + Description string `json:"Description"` + SortOrder int `json:"SortOrder"` + UseGuidedFailure bool `json:"UseGuidedFailure"` + AllowDynamicInfrastructure bool `json:"AllowDynamicInfrastructure"` } func (t *Environment) Validate() error { diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/feed.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/feed.go new file mode 100644 index 000000000..4c7bb22d3 --- /dev/null +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/feed.go @@ -0,0 +1,141 @@ +package octopusdeploy + +import ( + "fmt" + + "github.com/dghubble/sling" + "gopkg.in/go-playground/validator.v9" +) + +type FeedService struct { + sling *sling.Sling +} + +func NewFeedService(sling *sling.Sling) *FeedService { + return &FeedService{ + sling: sling, + } +} + +type Feeds struct { + Items []Feed `json:"Items"` + PagedResults +} + +type Feed struct { + ID string `json:"Id"` + Name string `json:"Name"` + FeedType string `json:"FeedType"` + DownloadAttempts int `json:"DownloadAttempts"` + DownloadRetryBackoffSeconds int `json:"DownloadRetryBackoffSeconds"` + FeedUri string `json:"FeedUri"` + EnhancedMode bool `json:"EnhancedMode"` + Username string `json:"Username"` + Password SensitiveValue `json:"Password"` +} + +func (t *Feed) Validate() error { + validate := validator.New() + + err := validate.Struct(t) + + if err != nil { + return err + } + + return nil +} + +func NewFeed(name, feedType string, feedUri string) *Feed { + return &Feed{ + Name: name, + FeedType: feedType, + FeedUri: feedUri, + } +} + +func (s *FeedService) Get(feedId string) (*Feed, error) { + path := fmt.Sprintf("feeds/%s", feedId) + resp, err := apiGet(s.sling, new(Feed), path) + + if err != nil { + return nil, err + } + + return resp.(*Feed), nil +} + +func (s *FeedService) GetAll() (*[]Feed, error) { + var p []Feed + + path := "feeds" + + loadNextPage := true + + for loadNextPage { + resp, err := apiGet(s.sling, new(Feeds), path) + + if err != nil { + return nil, err + } + + r := resp.(*Feeds) + + for _, item := range r.Items { + p = append(p, item) + } + + path, loadNextPage = LoadNextPage(r.PagedResults) + } + + return &p, nil +} + +func (s *FeedService) GetByName(feedName string) (*Feed, error) { + var foundFeed Feed + feeds, err := s.GetAll() + + if err != nil { + return nil, err + } + + for _, feed := range *feeds { + if feed.Name == feedName { + return &feed, nil + } + } + + return &foundFeed, fmt.Errorf("no feed found with feed name %s", feedName) +} + +func (s *FeedService) Add(feed *Feed) (*Feed, error) { + resp, err := apiAdd(s.sling, feed, new(Feed), "feeds") + + if err != nil { + return nil, err + } + + return resp.(*Feed), nil +} + +func (s *FeedService) Delete(feedId string) error { + path := fmt.Sprintf("feeds/%s", feedId) + err := apiDelete(s.sling, path) + + if err != nil { + return err + } + + return nil +} + +func (s *FeedService) Update(feed *Feed) (*Feed, error) { + path := fmt.Sprintf("feeds/%s", feed.ID) + resp, err := apiUpdate(s.sling, feed, new(Feed), path) + + if err != nil { + return nil, err + } + + return resp.(*Feed), nil +} diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/library_variable_set.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/library_variable_set.go index 55b639234..e6567f0ec 100644 --- a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/library_variable_set.go +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/library_variable_set.go @@ -24,21 +24,21 @@ type LibraryVariableSets struct { } type LibraryVariableSet struct { - ID string `json:"Id,omitempty"` - Name string `json:"Name" validate:"required"` - Description string `json:"Description,omitempty"` - VariableSetId string `json:"VariableSetId,omitempty"` - ContentType VariableSetContentType `json:"ContentType" validate:"required"` + ID string `json:"Id,omitempty"` + Name string `json:"Name" validate:"required"` + Description string `json:"Description,omitempty"` + VariableSetId string `json:"VariableSetId,omitempty"` + ContentType VariableSetContentType `json:"ContentType" validate:"required"` + Templates []ActionTemplateParameter `json:"Templates,omitempty"` } type VariableSetContentType string const ( - VariableSetContentType_Variables = VariableSetContentType("Variables") + VariableSetContentType_Variables = VariableSetContentType("Variables") VariableSetContentType_ScriptModule = VariableSetContentType("ScriptModule") ) - func NewLibraryVariableSet(name string) *LibraryVariableSet { return &LibraryVariableSet{ Name: name, @@ -68,7 +68,7 @@ func (s *LibraryVariableSetService) Get(libraryVariableSetID string) (*LibraryVa // GetAll returns all libraryVariableSets in Octopus Deploy func (s *LibraryVariableSetService) GetAll() (*[]LibraryVariableSet, error) { - return s.get(""); + return s.get("") } func (s *LibraryVariableSetService) get(query string) (*[]LibraryVariableSet, error) { diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/lifecycles.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/lifecycles.go index f306660b4..43fa41c94 100644 --- a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/lifecycles.go +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/lifecycles.go @@ -35,7 +35,7 @@ type Lifecycle struct { type RetentionUnit string const ( - RetentionUnit_Days = RetentionUnit("Days") + RetentionUnit_Days = RetentionUnit("Days") RetentionUnit_Items = RetentionUnit("Items") ) @@ -46,26 +46,25 @@ type RetentionPeriod struct { } type Phase struct { - ID string `json:"Id,omitempty"` - Name string `json:"Name" validate:"required"` - MinimumEnvironmentsBeforePromotion int32 `json:"MinimumEnvironmentsBeforePromotion"` - IsOptionalPhase bool `json:"IsOptionalPhase"` + ID string `json:"Id,omitempty"` + Name string `json:"Name" validate:"required"` + MinimumEnvironmentsBeforePromotion int32 `json:"MinimumEnvironmentsBeforePromotion"` + IsOptionalPhase bool `json:"IsOptionalPhase"` ReleaseRetentionPolicy *RetentionPeriod `json:"ReleaseRetentionPolicy"` TentacleRetentionPolicy *RetentionPeriod `json:"TentacleRetentionPolicy"` - AutomaticDeploymentTargets []string `json:"AutomaticDeploymentTargets"` - OptionalDeploymentTargets []string `json:"OptionalDeploymentTargets"` + AutomaticDeploymentTargets []string `json:"AutomaticDeploymentTargets"` + OptionalDeploymentTargets []string `json:"OptionalDeploymentTargets"` } - func NewLifecycle(name string) *Lifecycle { return &Lifecycle{ Name: name, Phases: []Phase{}, - TentacleRetentionPolicy:RetentionPeriod{ - Unit:RetentionUnit_Days, + TentacleRetentionPolicy: RetentionPeriod{ + Unit: RetentionUnit_Days, }, - ReleaseRetentionPolicy:RetentionPeriod{ - Unit:RetentionUnit_Days, + ReleaseRetentionPolicy: RetentionPeriod{ + Unit: RetentionUnit_Days, }, } } @@ -108,7 +107,7 @@ func (s *LifecycleService) Get(LifecycleID string) (*Lifecycle, error) { // GetAll returns all lifecycles in Octopus Deploy func (s *LifecycleService) GetAll() (*[]Lifecycle, error) { - return s.get(""); + return s.get("") } func (s *LifecycleService) get(query string) (*[]Lifecycle, error) { diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/octopusdeploy.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/octopusdeploy.go index 12b1ed85e..6db343a68 100644 --- a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/octopusdeploy.go +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/octopusdeploy.go @@ -13,17 +13,20 @@ import ( type Client struct { sling *sling.Sling // Octopus Deploy API Services + Account *AccountService DeploymentProcess *DeploymentProcessService ProjectGroup *ProjectGroupService Project *ProjectService ProjectTrigger *ProjectTriggerService Environment *EnvironmentService + Feed *FeedService Variable *VariableService MachinePolicy *MachinePolicyService Machine *MachineService Lifecycle *LifecycleService LibraryVariableSet *LibraryVariableSetService Interruption *InterruptionsService + TagSet *TagSetService } // NewClient returns a new Client. @@ -34,17 +37,20 @@ func NewClient(httpClient *http.Client, octopusURL, octopusAPIKey string) *Clien base := sling.New().Client(httpClient).Base(baseURLWithAPI).Set("X-Octopus-ApiKey", octopusAPIKey) return &Client{ sling: base, + Account: NewAccountService(base.New()), DeploymentProcess: NewDeploymentProcessService(base.New()), ProjectGroup: NewProjectGroupService(base.New()), Project: NewProjectService(base.New()), ProjectTrigger: NewProjectTriggerService(base.New()), Environment: NewEnvironmentService(base.New()), + Feed: NewFeedService(base.New()), Variable: NewVariableService(base.New()), MachinePolicy: NewMachinePolicyService(base.New()), Machine: NewMachineService(base.New()), Lifecycle: NewLifecycleService(base.New()), LibraryVariableSet: NewLibraryVariableSetService(base.New()), Interruption: NewInterruptionService(base.New()), + TagSet: NewTagSetService(base.New()), } } diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/tag_set.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/tag_set.go new file mode 100644 index 000000000..68e0b1107 --- /dev/null +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/tag_set.go @@ -0,0 +1,139 @@ +package octopusdeploy + +import ( + "fmt" + + "github.com/dghubble/sling" + "gopkg.in/go-playground/validator.v9" +) + +type TagSetService struct { + sling *sling.Sling +} + +func NewTagSetService(sling *sling.Sling) *TagSetService { + return &TagSetService{ + sling: sling, + } +} + +type TagSets struct { + Items []TagSet `json:"Items"` + PagedResults +} + +type TagSet struct { + ID string `json:"Id"` + Name string `json:"Name"` + Tags []Tag `json:"Tags,omitempty"` +} + +type Tag struct { + ID string `json:"Id"` + Name string `json:"Name"` + Color string `json:"Color"` +} + +func (t *TagSet) Validate() error { + validate := validator.New() + + err := validate.Struct(t) + + if err != nil { + return err + } + + return nil +} + +func NewTagSet(name string) *TagSet { + return &TagSet{ + Name: name, + } +} + +func (s *TagSetService) Get(tagSetId string) (*TagSet, error) { + path := fmt.Sprintf("tagSets/%s", tagSetId) + resp, err := apiGet(s.sling, new(TagSet), path) + + if err != nil { + return nil, err + } + + return resp.(*TagSet), nil +} + +func (s *TagSetService) GetAll() (*[]TagSet, error) { + var p []TagSet + + path := "tagSets" + + loadNextPage := true + + for loadNextPage { + resp, err := apiGet(s.sling, new(TagSets), path) + + if err != nil { + return nil, err + } + + r := resp.(*TagSets) + + for _, item := range r.Items { + p = append(p, item) + } + + path, loadNextPage = LoadNextPage(r.PagedResults) + } + + return &p, nil +} + +func (s *TagSetService) GetByName(tagSetName string) (*TagSet, error) { + var foundTagSet TagSet + tagSets, err := s.GetAll() + + if err != nil { + return nil, err + } + + for _, tagSet := range *tagSets { + if tagSet.Name == tagSetName { + return &tagSet, nil + } + } + + return &foundTagSet, fmt.Errorf("no tagSet found with tagSet name %s", tagSetName) +} + +func (s *TagSetService) Add(tagSet *TagSet) (*TagSet, error) { + resp, err := apiAdd(s.sling, tagSet, new(TagSet), "tagSets") + + if err != nil { + return nil, err + } + + return resp.(*TagSet), nil +} + +func (s *TagSetService) Delete(tagSetId string) error { + path := fmt.Sprintf("tagSets/%s", tagSetId) + err := apiDelete(s.sling, path) + + if err != nil { + return err + } + + return nil +} + +func (s *TagSetService) Update(tagSet *TagSet) (*TagSet, error) { + path := fmt.Sprintf("tagSets/%s", tagSet.ID) + resp, err := apiUpdate(s.sling, tagSet, new(TagSet), path) + + if err != nil { + return nil, err + } + + return resp.(*TagSet), nil +} diff --git a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/variables.go b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/variables.go index 27ccdba22..2fc96f5f9 100644 --- a/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/variables.go +++ b/vendor/github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy/variables.go @@ -38,6 +38,11 @@ type Variable struct { IsSensitive bool `json:"IsSensitive"` } +type SensitiveValue struct { + HasValue bool `json:"HasValue"` + NewValue string `json:"NewValue"` +} + type VariableScope struct { Project []string `json:"Project,omitempty"` Environment []string `json:"Environment,omitempty"` diff --git a/vendor/modules.txt b/vendor/modules.txt index ffbcce8ce..63dbf82e9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/OctopusDeploy/go-octopusdeploy v0.0.0-20190101071024-032701337fa6 +# github.com/OctopusDeploy/go-octopusdeploy v1.0.0 github.com/OctopusDeploy/go-octopusdeploy/octopusdeploy # github.com/agext/levenshtein v1.2.1 github.com/agext/levenshtein