From 77102655faf8071358cc7497128e6bc27a037bc6 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 24 Apr 2020 09:37:04 +0200 Subject: [PATCH 1/2] New Resource: `teamcity_feature_golang` --- teamcity/provider.go | 1 + teamcity/resource_feature_golang.go | 104 ++++++++++++++++++++ website/docs/r/feature_golang.html.markdown | 50 ++++++++++ website/teamcity.erb | 4 + 4 files changed, 159 insertions(+) create mode 100644 teamcity/resource_feature_golang.go create mode 100644 website/docs/r/feature_golang.html.markdown diff --git a/teamcity/provider.go b/teamcity/provider.go index a83089df..044cadde 100644 --- a/teamcity/provider.go +++ b/teamcity/provider.go @@ -20,6 +20,7 @@ func Provider() terraform.ResourceProvider { "teamcity_agent_requirement": resourceAgentRequirement(), "teamcity_feature_commit_status_publisher": resourceFeatureCommitStatusPublisher(), "teamcity_group": resourceGroup(), + "teamcity_feature_golang": resourceFeatureGolang(), }, DataSourcesMap: map[string]*schema.Resource{ "teamcity_project": dataSourceProject(), diff --git a/teamcity/resource_feature_golang.go b/teamcity/resource_feature_golang.go new file mode 100644 index 00000000..52b2b172 --- /dev/null +++ b/teamcity/resource_feature_golang.go @@ -0,0 +1,104 @@ +package teamcity + +import ( + "fmt" + "log" + "strings" + + api "github.com/cvbarros/go-teamcity/teamcity" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceFeatureGolang() *schema.Resource { + return &schema.Resource{ + Create: resourceFeatureGolangCreate, + Read: resourceFeatureGolangRead, + Delete: resourceFeatureGolangDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "build_config_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceFeatureGolangCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + + buildConfigId := d.Get("build_config_id").(string) + + // validates the Build Configuration exists + if _, err := client.BuildTypes.GetByID(buildConfigId); err != nil { + return fmt.Errorf("invalid build_config_id %q - Build configuration does not exist", buildConfigId) + } + + service := client.BuildFeatureService(buildConfigId) + feature := api.NewFeatureGolang() + feature.SetBuildTypeID(buildConfigId) + createdService, err := service.Create(feature) + if err != nil { + return err + } + + id := fmt.Sprintf("%s|%s", buildConfigId, createdService.ID()) + d.SetId(id) + + return resourceFeatureGolangRead(d, meta) +} + +func resourceFeatureGolangRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + + id, err := ParseFeatureGolangID(d.Id()) + if err != nil { + return err + } + + service := client.BuildFeatureService(id.BuildConfigID) + if _, err := service.GetByID(id.FeatureID); err != nil { + // handles this being deleted outside of TF + if isNotFoundError(err) { + log.Printf("[DEBUG] Build Feature Golang was not found - removing from state!") + d.SetId("") + return nil + } + + return err + } + + d.Set("build_config_id", id.BuildConfigID) + + return nil +} + +func resourceFeatureGolangDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + svr := client.BuildFeatureService(d.Get("build_config_id").(string)) + + return svr.Delete(d.Id()) +} + +type FeatureGolangId struct { + BuildConfigID string + FeatureID string +} + +func ParseFeatureGolangID(input string) (*FeatureGolangId, error) { + // Format: 'BuildConfigID|FeatureID' + segments := strings.Split(input, "|") + if len(segments) != 2 { + return nil, fmt.Errorf("Expected 2 segments but got %d", len(segments)) + } + + id := FeatureGolangId{ + BuildConfigID: segments[0], + FeatureID: segments[1], + } + return &id, nil +} diff --git a/website/docs/r/feature_golang.html.markdown b/website/docs/r/feature_golang.html.markdown new file mode 100644 index 00000000..6d57c355 --- /dev/null +++ b/website/docs/r/feature_golang.html.markdown @@ -0,0 +1,50 @@ +--- +subcategory: "Build Configurations" +layout: teamcity +page_title: "TeamCity: Resource - teamcity_feature_golang" +description: |- + Manages an Golang Build Feature for a Build Configuration +--- + +# teamcity_feature_golang + +Manages an Golang Build Feature for a Build Configuration + +## Example Usage + +```hcl +resource "teamcity_project" "example" { + name = "Example Project" +} + +resource "teamcity_build_config" "example" { + name = "Example Build" + project_id = teamcity_project.example.id +} + +resource "teamcity_feature_golang" "example" { + build_config_id = teamcity_build_config.example.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `build_config_id` - (Required) Specifies the ID of the Build Configuration for which a Golang Build Feature should be configured. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The auto-generated ID of the Golang Build Feature. + +## Import + +Golang Build Features can be imported using their ID, e.g. + +``` +$ terraform import teamcity_feature_golang.example "ProjectID|golang" +``` + +-> **Note:** This is a Terraform specific ID comprised of "ProjectID|FeatureID" - where featureID is likely `golang` diff --git a/website/teamcity.erb b/website/teamcity.erb index 4f613546..9e7cc706 100644 --- a/website/teamcity.erb +++ b/website/teamcity.erb @@ -43,6 +43,10 @@ teamcity_build_trigger_vcs +
  • + teamcity_feature_golang +
  • +
  • teamcity_project
  • From d9995c0d053e13ef41a2805dbcc07c99f9ba3c48 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 24 Apr 2020 10:12:41 +0200 Subject: [PATCH 2/2] r/feature_golang: adding acctests --- teamcity/resource_feature_golang.go | 15 +++- teamcity/resource_feature_golang_test.go | 98 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 teamcity/resource_feature_golang_test.go diff --git a/teamcity/resource_feature_golang.go b/teamcity/resource_feature_golang.go index 52b2b172..bf445f83 100644 --- a/teamcity/resource_feature_golang.go +++ b/teamcity/resource_feature_golang.go @@ -79,9 +79,20 @@ func resourceFeatureGolangRead(d *schema.ResourceData, meta interface{}) error { func resourceFeatureGolangDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*api.Client) - svr := client.BuildFeatureService(d.Get("build_config_id").(string)) - return svr.Delete(d.Id()) + id, err := ParseFeatureGolangID(d.Id()) + if err != nil { + return err + } + + service := client.BuildFeatureService(id.BuildConfigID) + if err := service.Delete(id.FeatureID); err != nil { + if !isNotFoundError(err) { + return err + } + } + + return nil } type FeatureGolangId struct { diff --git a/teamcity/resource_feature_golang_test.go b/teamcity/resource_feature_golang_test.go new file mode 100644 index 00000000..92af9682 --- /dev/null +++ b/teamcity/resource_feature_golang_test.go @@ -0,0 +1,98 @@ +package teamcity_test + +import ( + "fmt" + "strings" + "testing" + + api "github.com/cvbarros/go-teamcity/teamcity" + "github.com/cvbarros/terraform-provider-teamcity/teamcity" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccTeamcityFeatureGolang_Basic(t *testing.T) { + resName := "teamcity_feature_golang.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBuildFeatureGolangDestroy, + Steps: []resource.TestStep{ + { + Config: TestAccBuildFeatureGolang_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckBuildFeatureGolangExists(resName), + ), + }, + { + ResourceName: resName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckBuildFeatureGolangDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*api.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type != "teamcity_feature_github" { + continue + } + + id, err := teamcity.ParseFeatureGolangID(rs.Primary.ID) + if err != nil { + return err + } + + srv := client.BuildFeatureService(id.BuildConfigID) + if _, err := srv.GetByID(id.FeatureID); err != nil { + if strings.Contains(err.Error(), "404") { + continue + } + + return fmt.Errorf("Received an error retrieving the Golang Build Feature: %s", err) + } + + return fmt.Errorf("Golang Build Feature still exists") + } + return nil +} + +func testAccCheckBuildFeatureGolangExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*api.Client) + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + id, err := teamcity.ParseFeatureGolangID(rs.Primary.ID) + if err != nil { + return err + } + + srv := client.BuildFeatureService(id.BuildConfigID) + if _, err := srv.GetByID(id.FeatureID); err != nil { + return fmt.Errorf("Received an error retrieving Golang Build Feature: %s", err) + } + + return nil + } +} + +const TestAccBuildFeatureGolang_basic = ` +resource "teamcity_project" "test" { + name = "Build Feature" +} + +resource "teamcity_build_config" "test" { + name = "BuildConfig" + project_id = teamcity_project.test.id +} + +resource "teamcity_feature_golang" "test" { + build_config_id = teamcity_build_config.test.id +} +`