Skip to content

Commit 2b4bee5

Browse files
authored
Merge pull request #284 from giorgioazzinnaro/deploy-token
Deploy token
2 parents feae9c9 + 5f975c0 commit 2b4bee5

File tree

5 files changed

+427
-8
lines changed

5 files changed

+427
-8
lines changed

gitlab/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func Provider() terraform.ResourceProvider {
7575
"gitlab_project_push_rules": resourceGitlabProjectPushRules(),
7676
"gitlab_deploy_key": resourceGitlabDeployKey(),
7777
"gitlab_deploy_key_enable": resourceGitlabDeployEnableKey(),
78+
"gitlab_deploy_token": resourceGitlabDeployToken(),
7879
"gitlab_user": resourceGitlabUser(),
7980
"gitlab_project_membership": resourceGitlabProjectMembership(),
8081
"gitlab_group_membership": resourceGitlabGroupMembership(),
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"strconv"
8+
"time"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
11+
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
12+
"github.com/xanzy/go-gitlab"
13+
)
14+
15+
func resourceGitlabDeployToken() *schema.Resource {
16+
return &schema.Resource{
17+
Create: resourceGitlabDeployTokenCreate,
18+
Read: resourceGitlabDeployTokenRead,
19+
Delete: resourceGitlabDeployTokenDelete,
20+
21+
Schema: map[string]*schema.Schema{
22+
"project": {
23+
Type: schema.TypeString,
24+
Optional: true,
25+
ExactlyOneOf: []string{"project", "group"},
26+
ForceNew: true,
27+
},
28+
"group": {
29+
Type: schema.TypeString,
30+
Optional: true,
31+
ExactlyOneOf: []string{"project", "group"},
32+
ForceNew: true,
33+
},
34+
"name": {
35+
Type: schema.TypeString,
36+
Required: true,
37+
ForceNew: true,
38+
},
39+
"username": {
40+
Type: schema.TypeString,
41+
Optional: true,
42+
Computed: true,
43+
ForceNew: true,
44+
},
45+
"expires_at": {
46+
Type: schema.TypeString,
47+
Optional: true,
48+
ValidateFunc: validation.ValidateRFC3339TimeString,
49+
ForceNew: true,
50+
},
51+
"scopes": {
52+
Type: schema.TypeSet,
53+
Required: true,
54+
ForceNew: true,
55+
Elem: &schema.Schema{
56+
Type: schema.TypeString,
57+
ValidateFunc: validation.StringInSlice([]string{"read_registry", "read_repository"}, false),
58+
},
59+
},
60+
61+
"token": {
62+
Type: schema.TypeString,
63+
Computed: true,
64+
Sensitive: true,
65+
},
66+
},
67+
}
68+
}
69+
70+
func resourceGitlabDeployTokenCreate(d *schema.ResourceData, meta interface{}) error {
71+
client := meta.(*gitlab.Client)
72+
project, isProject := d.GetOk("project")
73+
group, isGroup := d.GetOk("group")
74+
75+
var expiresAt time.Time
76+
var err error
77+
78+
if exp, ok := d.GetOk("expires_at"); ok {
79+
expiresAt, err = time.Parse(time.RFC3339, exp.(string))
80+
if err != nil {
81+
return fmt.Errorf("Invalid expires_at date: %v", err)
82+
}
83+
}
84+
85+
scopes := stringSetToStringSlice(d.Get("scopes").(*schema.Set))
86+
87+
var deployToken *gitlab.DeployToken
88+
89+
if isProject {
90+
options := &gitlab.CreateProjectDeployTokenOptions{
91+
Name: gitlab.String(d.Get("name").(string)),
92+
Username: gitlab.String(d.Get("username").(string)),
93+
ExpiresAt: gitlab.Time(expiresAt),
94+
Scopes: *scopes,
95+
}
96+
97+
log.Printf("[DEBUG] Create GitLab deploy token %s in project %s", *options.Name, project.(string))
98+
99+
deployToken, _, err = client.DeployTokens.CreateProjectDeployToken(project, options)
100+
101+
} else if isGroup {
102+
options := &gitlab.CreateGroupDeployTokenOptions{
103+
Name: gitlab.String(d.Get("name").(string)),
104+
Username: gitlab.String(d.Get("username").(string)),
105+
ExpiresAt: gitlab.Time(expiresAt),
106+
Scopes: *scopes,
107+
}
108+
109+
log.Printf("[DEBUG] Create GitLab deploy token %s in group %s", *options.Name, group.(string))
110+
111+
deployToken, _, err = client.DeployTokens.CreateGroupDeployToken(group, options)
112+
}
113+
114+
if err != nil {
115+
return err
116+
}
117+
118+
d.SetId(fmt.Sprintf("%d", deployToken.ID))
119+
120+
// Token is only available on creation
121+
d.Set("token", deployToken.Token)
122+
d.Set("username", deployToken.Username)
123+
124+
return nil
125+
}
126+
127+
func resourceGitlabDeployTokenRead(d *schema.ResourceData, meta interface{}) error {
128+
client := meta.(*gitlab.Client)
129+
project, isProject := d.GetOk("project")
130+
group, isGroup := d.GetOk("group")
131+
deployTokenID, err := strconv.Atoi(d.Id())
132+
if err != nil {
133+
return err
134+
}
135+
136+
var deployTokens []*gitlab.DeployToken
137+
138+
if isProject {
139+
log.Printf("[DEBUG] Read GitLab deploy token %d in project %s", deployTokenID, project.(string))
140+
deployTokens, _, err = client.DeployTokens.ListProjectDeployTokens(project, nil)
141+
142+
} else if isGroup {
143+
log.Printf("[DEBUG] Read GitLab deploy token %d in group %s", deployTokenID, group.(string))
144+
deployTokens, _, err = client.DeployTokens.ListGroupDeployTokens(group, nil)
145+
}
146+
if err != nil {
147+
return err
148+
}
149+
150+
for _, token := range deployTokens {
151+
if token.ID == deployTokenID {
152+
d.Set("name", token.Name)
153+
d.Set("username", token.Username)
154+
d.Set("expires_at", token.ExpiresAt.Format(time.RFC3339))
155+
156+
for _, scope := range token.Scopes {
157+
if scope == "read_repository" {
158+
d.Set("scopes.read_repository", true)
159+
}
160+
161+
if scope == "read_registry" {
162+
d.Set("scopes.read_registry", true)
163+
}
164+
}
165+
}
166+
}
167+
168+
return nil
169+
}
170+
171+
func resourceGitlabDeployTokenDelete(d *schema.ResourceData, meta interface{}) error {
172+
client := meta.(*gitlab.Client)
173+
project, isProject := d.GetOk("project")
174+
group, isGroup := d.GetOk("group")
175+
deployTokenID, err := strconv.Atoi(d.Id())
176+
if err != nil {
177+
return err
178+
}
179+
180+
var response *gitlab.Response
181+
182+
if isProject {
183+
log.Printf("[DEBUG] Delete GitLab deploy token %d in project %s", deployTokenID, project.(string))
184+
response, err = client.DeployTokens.DeleteProjectDeployToken(project, deployTokenID)
185+
186+
} else if isGroup {
187+
log.Printf("[DEBUG] Delete GitLab deploy token %d in group %s", deployTokenID, group.(string))
188+
response, err = client.DeployTokens.DeleteGroupDeployToken(group, deployTokenID)
189+
}
190+
if err != nil {
191+
return err
192+
}
193+
194+
// StatusNoContent = 204
195+
// Success with no body
196+
if response.StatusCode != http.StatusNoContent {
197+
return fmt.Errorf("Invalid status code returned: %s", response.Status)
198+
}
199+
200+
return nil
201+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"strconv"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-sdk/terraform"
12+
"github.com/xanzy/go-gitlab"
13+
)
14+
15+
func TestAccGitlabDeployToken_basic(t *testing.T) {
16+
var deployToken gitlab.DeployToken
17+
rInt := acctest.RandInt()
18+
19+
resource.Test(t, resource.TestCase{
20+
PreCheck: func() { testAccPreCheck(t) },
21+
Providers: testAccProviders,
22+
CheckDestroy: testAccCheckGitlabDeployTokenDestroy,
23+
Steps: []resource.TestStep{
24+
{
25+
Config: testAccGitlabDeployTokenConfig(rInt),
26+
Check: resource.ComposeTestCheckFunc(
27+
testAccCheckGitlabDeployTokenExists("gitlab_deploy_token.foo", &deployToken),
28+
testAccCheckGitlabDeployTokenAttributes(&deployToken, &testAccCheckGitlabDeployTokenExpectedAttributes{
29+
Name: fmt.Sprintf("deployToken-%d", rInt),
30+
Username: "my-username",
31+
}),
32+
),
33+
},
34+
},
35+
})
36+
}
37+
38+
func testAccCheckGitlabDeployTokenExists(n string, deployToken *gitlab.DeployToken) resource.TestCheckFunc {
39+
return func(s *terraform.State) error {
40+
rs, ok := s.RootModule().Resources[n]
41+
if !ok {
42+
return fmt.Errorf("Not Found: %s", n)
43+
}
44+
45+
deployTokenID, err := strconv.Atoi(rs.Primary.ID)
46+
if err != nil {
47+
return err
48+
}
49+
50+
conn := testAccProvider.Meta().(*gitlab.Client)
51+
52+
projectName := rs.Primary.Attributes["project"]
53+
groupName := rs.Primary.Attributes["group"]
54+
55+
var gotDeployTokens []*gitlab.DeployToken
56+
57+
if projectName != "" {
58+
gotDeployTokens, _, err = conn.DeployTokens.ListProjectDeployTokens(projectName, nil)
59+
} else if groupName != "" {
60+
gotDeployTokens, _, err = conn.DeployTokens.ListGroupDeployTokens(groupName, nil)
61+
} else {
62+
return fmt.Errorf("No project or group ID is set")
63+
}
64+
65+
if err != nil {
66+
return err
67+
}
68+
69+
for _, token := range gotDeployTokens {
70+
if token.ID == deployTokenID {
71+
*deployToken = *token
72+
return nil
73+
}
74+
}
75+
76+
return fmt.Errorf("Deploy Token doesn't exist")
77+
}
78+
}
79+
80+
type testAccCheckGitlabDeployTokenExpectedAttributes struct {
81+
Name string
82+
Username string
83+
}
84+
85+
func testAccCheckGitlabDeployTokenAttributes(deployToken *gitlab.DeployToken, want *testAccCheckGitlabDeployTokenExpectedAttributes) resource.TestCheckFunc {
86+
return func(s *terraform.State) error {
87+
88+
if deployToken.Name != want.Name {
89+
return fmt.Errorf("got name %q; want %q", deployToken.Name, want.Name)
90+
}
91+
92+
if deployToken.Username != want.Username {
93+
return fmt.Errorf("got username %q; want %q", deployToken.Username, want.Username)
94+
}
95+
96+
return nil
97+
}
98+
}
99+
100+
func testAccCheckGitlabDeployTokenDestroy(s *terraform.State) error {
101+
conn := testAccProvider.Meta().(*gitlab.Client)
102+
103+
for _, rs := range s.RootModule().Resources {
104+
if rs.Type != "gitlab_deploy_token" {
105+
continue
106+
}
107+
108+
deployTokenID, err := strconv.Atoi(rs.Primary.ID)
109+
if err != nil {
110+
return err
111+
}
112+
113+
project := rs.Primary.Attributes["project"]
114+
group := rs.Primary.Attributes["group"]
115+
116+
var gotDeployTokens []*gitlab.DeployToken
117+
var resp *gitlab.Response
118+
119+
if project != "" {
120+
gotDeployTokens, resp, err = conn.DeployTokens.ListProjectDeployTokens(project, nil)
121+
} else if group != "" {
122+
gotDeployTokens, resp, err = conn.DeployTokens.ListGroupDeployTokens(group, nil)
123+
} else {
124+
return fmt.Errorf("somehow neither project nor group were set")
125+
}
126+
127+
if err == nil {
128+
for _, token := range gotDeployTokens {
129+
if token.ID == deployTokenID {
130+
return fmt.Errorf("Deploy token still exists")
131+
}
132+
}
133+
}
134+
135+
if resp.StatusCode != http.StatusNotFound {
136+
return err
137+
}
138+
}
139+
140+
return nil
141+
}
142+
143+
func testAccGitlabDeployTokenConfig(rInt int) string {
144+
return fmt.Sprintf(`
145+
resource "gitlab_project" "foo" {
146+
name = "foo-%d"
147+
description = "Terraform acceptance test"
148+
149+
# So that acceptance tests can be run in a gitlab organization
150+
# with no billing
151+
visibility_level = "public"
152+
}
153+
154+
resource "gitlab_deploy_token" "foo" {
155+
project = "${gitlab_project.foo.id}"
156+
name = "deployToken-%d"
157+
username = "my-username"
158+
159+
expires_at = "2021-03-14T07:20:50Z"
160+
161+
scopes = [
162+
"read_registry",
163+
"read_repository",
164+
]
165+
}
166+
`, rInt, rInt)
167+
}

0 commit comments

Comments
 (0)