From 4859ef1092f14e1951ae8111c13f43ada6ab0516 Mon Sep 17 00:00:00 2001 From: Martin Fernandez Date: Sun, 5 May 2019 21:49:50 -0300 Subject: [PATCH] Add tag protection --- gitlab/provider.go | 1 + gitlab/resource_gitlab_tag_protection.go | 121 +++++++++++++ gitlab/resource_gitlab_tag_protection_test.go | 166 ++++++++++++++++++ .../docs/r/branch_protection.html.markdown | 2 +- website/docs/r/tag_protection.html.markdown | 31 ++++ 5 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 gitlab/resource_gitlab_tag_protection.go create mode 100644 gitlab/resource_gitlab_tag_protection_test.go create mode 100644 website/docs/r/tag_protection.html.markdown diff --git a/gitlab/provider.go b/gitlab/provider.go index 4a62dc4fa..786cc955c 100644 --- a/gitlab/provider.go +++ b/gitlab/provider.go @@ -49,6 +49,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "gitlab_branch_protection": resourceGitlabBranchProtection(), + "gitlab_tag_protection": resourceGitlabTagProtection(), "gitlab_group": resourceGitlabGroup(), "gitlab_project": resourceGitlabProject(), "gitlab_label": resourceGitlabLabel(), diff --git a/gitlab/resource_gitlab_tag_protection.go b/gitlab/resource_gitlab_tag_protection.go new file mode 100644 index 000000000..5942ad591 --- /dev/null +++ b/gitlab/resource_gitlab_tag_protection.go @@ -0,0 +1,121 @@ +package gitlab + +import ( + "log" + "net/url" + + "github.com/hashicorp/terraform/helper/schema" + gitlab "github.com/xanzy/go-gitlab" +) + +func resourceGitlabTagProtection() *schema.Resource { + acceptedAccessLevels := make([]string, 0, len(accessLevelID)) + + for k := range accessLevelID { + acceptedAccessLevels = append(acceptedAccessLevels, k) + } + return &schema.Resource{ + Create: resourceGitlabTagProtectionCreate, + Read: resourceGitlabTagProtectionRead, + Delete: resourceGitlabTagProtectionDelete, + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "tag": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + }, + "create_access_level": { + Type: schema.TypeString, + ValidateFunc: validateValueFunc(acceptedAccessLevels), + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceGitlabTagProtectionCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + project := d.Get("project").(string) + tag := gitlab.String(d.Get("tag").(string)) + createAccessLevel := accessLevelID[d.Get("create_access_level").(string)] + + options := &gitlab.ProtectRepositoryTagsOptions{ + Name: tag, + CreateAccessLevel: &createAccessLevel, + } + + log.Printf("[DEBUG] create gitlab tag protection on %v for project %s", options.Name, project) + + tp, _, err := client.ProtectedTags.ProtectRepositoryTags(project, options) + if err != nil { + // Remove existing tag protection + _, err = client.ProtectedTags.UnprotectRepositoryTags(project, url.PathEscape(*tag)) + if err != nil { + return err + } + // Reprotect tag with updated values + tp, _, err = client.ProtectedTags.ProtectRepositoryTags(project, options) + if err != nil { + return err + } + } + + d.SetId(buildTwoPartID(&project, &tp.Name)) + + return resourceGitlabTagProtectionRead(d, meta) +} + +func resourceGitlabTagProtectionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + project, tag, err := projectAndTagFromID(d.Id()) + if err != nil { + return err + } + + log.Printf("[DEBUG] read gitlab tag protection for project %s, tag %s", project, tag) + + pt, response, err := client.ProtectedTags.GetProtectedTag(project, url.PathEscape(tag)) + if err != nil { + if response.StatusCode == 404 { + log.Printf("[WARN] removing project tag protection %s from state because it no longer exists in gitlab", tag) + d.SetId("") + return nil + } + + return err + } + + d.Set("project", project) + d.Set("tag", pt.Name) + d.Set("create_access_level", pt.CreateAccessLevels[0].AccessLevel) + + d.SetId(buildTwoPartID(&project, &pt.Name)) + + return nil +} + +func resourceGitlabTagProtectionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + project := d.Get("project").(string) + tag := d.Get("tag").(string) + + log.Printf("[DEBUG] Delete gitlab protected tag %s for project %s", tag, project) + + _, err := client.ProtectedTags.UnprotectRepositoryTags(project, url.PathEscape(tag)) + return err +} + +func projectAndTagFromID(id string) (string, string, error) { + project, tag, err := parseTwoPartID(id) + + if err != nil { + log.Printf("[WARN] cannot get group member id from input: %v", id) + } + return project, tag, err +} diff --git a/gitlab/resource_gitlab_tag_protection_test.go b/gitlab/resource_gitlab_tag_protection_test.go new file mode 100644 index 000000000..a7521c81d --- /dev/null +++ b/gitlab/resource_gitlab_tag_protection_test.go @@ -0,0 +1,166 @@ +package gitlab + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/xanzy/go-gitlab" +) + +func TestAccGitlabTagProtection_basic(t *testing.T) { + + var pt gitlab.ProtectedTag + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGitlabTagProtectionDestroy, + Steps: []resource.TestStep{ + // Create a project and Tag Protection with default options + { + Config: testAccGitlabTagProtectionConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabTagProtectionExists("gitlab_tag_protection.TagProtect", &pt), + testAccCheckGitlabTagProtectionAttributes(&pt, &testAccGitlabTagProtectionExpectedAttributes{ + Name: fmt.Sprintf("TagProtect-%d", rInt), + CreateAccessLevel: accessLevel[gitlab.DeveloperPermissions], + }), + ), + }, + // Update the Tag Protection + { + Config: testAccGitlabTagProtectionUpdateConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabTagProtectionExists("gitlab_tag_protection.TagProtect", &pt), + testAccCheckGitlabTagProtectionAttributes(&pt, &testAccGitlabTagProtectionExpectedAttributes{ + Name: fmt.Sprintf("TagProtect-%d", rInt), + CreateAccessLevel: accessLevel[gitlab.MasterPermissions], + }), + ), + }, + // Update the Tag Protection to get back to initial settings + { + Config: testAccGitlabTagProtectionConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabTagProtectionExists("gitlab_tag_protection.TagProtect", &pt), + testAccCheckGitlabTagProtectionAttributes(&pt, &testAccGitlabTagProtectionExpectedAttributes{ + Name: fmt.Sprintf("TagProtect-%d", rInt), + CreateAccessLevel: accessLevel[gitlab.DeveloperPermissions], + }), + ), + }, + }, + }) +} + +func testAccCheckGitlabTagProtectionExists(n string, pt *gitlab.ProtectedTag) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not Found: %s", n) + } + project, tag, err := projectAndTagFromID(rs.Primary.ID) + if err != nil { + return fmt.Errorf("Error in Splitting Project and Tag Ids") + } + + conn := testAccProvider.Meta().(*gitlab.Client) + + pts, _, err := conn.ProtectedTags.ListProtectedTags(project, nil) + if err != nil { + return err + } + for _, gotpt := range pts { + if gotpt.Name == tag { + *pt = *gotpt + return nil + } + } + return fmt.Errorf("Protected Tag does not exist") + } +} + +type testAccGitlabTagProtectionExpectedAttributes struct { + Name string + CreateAccessLevel string +} + +func testAccCheckGitlabTagProtectionAttributes(pt *gitlab.ProtectedTag, want *testAccGitlabTagProtectionExpectedAttributes) resource.TestCheckFunc { + return func(s *terraform.State) error { + if pt.Name != want.Name { + return fmt.Errorf("got name %q; want %q", pt.Name, want.Name) + } + + if pt.CreateAccessLevels[0].AccessLevel != accessLevelID[want.CreateAccessLevel] { + return fmt.Errorf("got Create access levels %q; want %q", pt.CreateAccessLevels[0].AccessLevel, accessLevelID[want.CreateAccessLevel]) + } + + return nil + } +} + +func testAccCheckGitlabTagProtectionDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*gitlab.Client) + var project string + var tag string + for _, rs := range s.RootModule().Resources { + if rs.Type == "gitlab_project" { + project = rs.Primary.ID + } else if rs.Type == "gitlab_tag_protection" { + tag = rs.Primary.ID + } + } + + pt, response, err := conn.ProtectedTags.GetProtectedTag(project, tag) + if err == nil { + if pt != nil { + return fmt.Errorf("project tag protection %s still exists", tag) + } + } + if response.StatusCode != 404 { + return err + } + return nil +} + +func testAccGitlabTagProtectionConfig(rInt int) string { + return fmt.Sprintf(` +resource "gitlab_project" "foo" { + name = "foo-%d" + description = "Terraform acceptance tests" + + # So that acceptance tests can be run in a gitlab organization + # with no billing + visibility_level = "public" +} + +resource "gitlab_tag_protection" "TagProtect" { + project = "${gitlab_project.foo.id}" + tag = "TagProtect-%d" + create_access_level = "developer" +} + `, rInt, rInt) +} + +func testAccGitlabTagProtectionUpdateConfig(rInt int) string { + return fmt.Sprintf(` +resource "gitlab_project" "foo" { + name = "foo-%d" + description = "Terraform acceptance tests" + + # So that acceptance tests can be run in a gitlab organization + # with no billing + visibility_level = "public" +} + +resource "gitlab_tag_protection" "TagProtect" { + project = "${gitlab_project.foo.id}" + tag = "TagProtect-%d" + create_access_level = "maintainer" +} + `, rInt, rInt) +} diff --git a/website/docs/r/branch_protection.html.markdown b/website/docs/r/branch_protection.html.markdown index 2495980cb..235fe562e 100644 --- a/website/docs/r/branch_protection.html.markdown +++ b/website/docs/r/branch_protection.html.markdown @@ -8,7 +8,7 @@ description: |- # gitlab\_branch_protection -This resource allows you to protect a specific branch by an access level so that the user with less access level cannot Merge/Push to the branch. +This resource allows you to protect a specific branch by an access level so that the user with less access level cannot Merge/Push to the branch. GitLab EE features to protect by group or user are not supported. ## Example Usage diff --git a/website/docs/r/tag_protection.html.markdown b/website/docs/r/tag_protection.html.markdown new file mode 100644 index 000000000..87b22d574 --- /dev/null +++ b/website/docs/r/tag_protection.html.markdown @@ -0,0 +1,31 @@ +--- +layout: "gitlab" +page_title: "GitLab: gitlab_tag_protection" +sidebar_current: "docs-gitlab-resource-tag_protection" +description: |- + Protects a tag by assigning access levels to it +--- + +# gitlab\_tag\_protection + +This resource allows you to protect a specific tag or wildcard by an access level so that the user with less access level cannot Create the tags. + +## Example Usage + +```hcl +resource "gitlab_tag_protection" "TagProtect" { + project = "12345" + tag = "TagProtected" + create_access_level = "developer" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project` - (Required) The id of the project. + +* `tag` - (Required) Name of the tag or wildcard. + +* `create_access_level` - (Required) One of five levels of access to the project. \ No newline at end of file