Skip to content

Commit d8e4ab4

Browse files
committed
Add the project access token
1 parent edda219 commit d8e4ab4

File tree

6 files changed

+428
-0
lines changed

6 files changed

+428
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# gitlab\_project\_access\_token
2+
3+
This resource allows you to create and manage Project Access Token for your GitLab projects.
4+
5+
6+
## Example Usage
7+
8+
```hcl
9+
resource "gitlab_deploy_token" "example" {
10+
project = "example/deploying"
11+
name = "Example project access token"
12+
expires_at = "2020-03-14"
13+
14+
scopes = [ "api" ]
15+
}
16+
```
17+
18+
## Argument Reference
19+
20+
The following arguments are supported:
21+
22+
* `project` - (Required, string) The id of the project to add the deploy token to.
23+
24+
* `name` - (Required, string) A name to describe the deploy token with.
25+
26+
* `expires_at` - (Optional, string) Time the token will expire it, YYYY-MM-DD format. Will not expire per default.
27+
28+
* `scopes` - (Required, set of strings) Valid values: `api`, `read_api`, `read_repository`, `write_repository`.
29+
30+
## Attributes Reference
31+
32+
The following attributes are exported in addition to the arguments listed above:
33+
34+
* `token` - The secret token. This is only populated when creating a new deploy token.
35+
36+
* `active` - True if the token is active.
37+
38+
* `created_at` - Time the token has been created, RFC3339 format.
39+
40+
* `revoked` - True if the token is revoked.
41+
42+
* `user_id` - The user_id associated to the token.

gitlab/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func Provider() *schema.Provider {
8484
"gitlab_group_membership": resourceGitlabGroupMembership(),
8585
"gitlab_project_variable": resourceGitlabProjectVariable(),
8686
"gitlab_group_variable": resourceGitlabGroupVariable(),
87+
"gitlab_project_access_token": resourceGitlabProjectAccessToken(),
8788
"gitlab_project_cluster": resourceGitlabProjectCluster(),
8889
"gitlab_service_slack": resourceGitlabServiceSlack(),
8990
"gitlab_service_jira": resourceGitlabServiceJira(),
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"strconv"
7+
"time"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
11+
gitlab "github.com/xanzy/go-gitlab"
12+
)
13+
14+
func resourceGitlabProjectAccessToken() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceGitlabProjectAccessTokenCreate,
17+
Read: resourceGitlabProjectAccessTokenRead,
18+
Delete: resourceGitlabProjectAccessTokenDelete,
19+
20+
Schema: map[string]*schema.Schema{
21+
"project": {
22+
Type: schema.TypeInt,
23+
Required: true,
24+
ForceNew: true,
25+
},
26+
"name": {
27+
Type: schema.TypeString,
28+
Required: true,
29+
ForceNew: true,
30+
},
31+
"scopes": {
32+
Type: schema.TypeSet,
33+
Required: true,
34+
ForceNew: true,
35+
Elem: &schema.Schema{
36+
Type: schema.TypeString,
37+
ValidateFunc: validation.StringInSlice([]string{"api", "read_api", "read_repository", "write_repository"}, false),
38+
},
39+
},
40+
"expires_at": {
41+
Type: schema.TypeString,
42+
Optional: true,
43+
ValidateFunc: func(i interface{}, k string) (warnings []string, errors []error) {
44+
v, ok := i.(string)
45+
if !ok {
46+
errors = append(errors, fmt.Errorf("expected type of %q to be string", k))
47+
return warnings, errors
48+
}
49+
50+
if _, err := time.Parse("2006-01-02", v); err != nil {
51+
errors = append(errors, fmt.Errorf("expected %q to be a valid YYYY-MM-DD date, got %q: %+v", k, i, err))
52+
}
53+
54+
return warnings, errors
55+
},
56+
ForceNew: true,
57+
},
58+
"token": {
59+
Type: schema.TypeString,
60+
Computed: true,
61+
Sensitive: true,
62+
},
63+
"active": {
64+
Type: schema.TypeBool,
65+
Computed: true,
66+
},
67+
"created_at": {
68+
Type: schema.TypeString,
69+
Computed: true,
70+
},
71+
"revoked": {
72+
Type: schema.TypeBool,
73+
Computed: true,
74+
},
75+
"user_id": {
76+
Type: schema.TypeInt,
77+
Computed: true,
78+
},
79+
},
80+
}
81+
}
82+
83+
func resourceGitlabProjectAccessTokenCreate(d *schema.ResourceData, meta interface{}) error {
84+
client := meta.(*gitlab.Client)
85+
project := d.Get("project").(int)
86+
options := &gitlab.CreateProjectAccessTokenOptions{
87+
Name: gitlab.String(d.Get("name").(string)),
88+
Scopes: *stringSetToStringSlice(d.Get("scopes").(*schema.Set)),
89+
}
90+
91+
log.Printf("[DEBUG] create gitlab ProjectAccessToken %s %s", *options.Name, options.Scopes)
92+
93+
if v, ok := d.GetOk("expires_at"); ok {
94+
parsedExpiresAt, err := time.Parse("2006-01-02", v.(string))
95+
if err != nil {
96+
return fmt.Errorf("Invalid expires_at date: %v", err)
97+
}
98+
options.ExpiresAt = &parsedExpiresAt
99+
log.Printf("[DEBUG] create gitlab ProjectAccessToken with expires_at %s", *options.ExpiresAt)
100+
}
101+
102+
projectAccessToken, _, err := client.ProjectAccessTokens.CreateProjectAccessToken(project, options)
103+
if err != nil {
104+
return err
105+
}
106+
107+
d.SetId(strconv.Itoa(projectAccessToken.ID))
108+
d.Set("token", projectAccessToken.Token)
109+
110+
return resourceGitlabProjectAccessTokenRead(d, meta)
111+
}
112+
113+
func resourceGitlabProjectAccessTokenRead(d *schema.ResourceData, meta interface{}) error {
114+
client := meta.(*gitlab.Client)
115+
project := d.Get("project").(int)
116+
projectAccessTokenID, err := strconv.Atoi(d.Id())
117+
118+
if err != nil {
119+
return fmt.Errorf("%s cannot be converted to int", d.Id())
120+
}
121+
122+
log.Printf("[DEBUG] read gitlab ProjectAccessToken %d, project ID %d", projectAccessTokenID, project)
123+
124+
//there is a slight possibility to not find an existing item, for example
125+
// 1. item is #101 (ie, in the 2nd page)
126+
// 2. I load first page (ie. I don't find my target item)
127+
// 3. A concurrent operation remove item 99 (ie, my target item shift to 1st page)
128+
// 4. a concurrent operation add an item
129+
// 5: I load 2nd page (ie. I don't find my target item)
130+
// 6. Total pages and total items properties are unchanged (from the perspective of the reader)
131+
132+
page := 1
133+
for page != 0 {
134+
projectAccessTokens, response, err := client.ProjectAccessTokens.ListProjectAccessTokens(project, &gitlab.ListProjectAccessTokensOptions{Page: page, PerPage: 100})
135+
if err != nil {
136+
return err
137+
}
138+
139+
for _, projectAccessToken := range projectAccessTokens {
140+
if projectAccessToken.ID == projectAccessTokenID {
141+
142+
d.Set("project", project)
143+
d.Set("name", projectAccessToken.Name)
144+
if projectAccessToken.ExpiresAt != nil {
145+
d.Set("expires_at", projectAccessToken.ExpiresAt.String())
146+
}
147+
d.Set("active", projectAccessToken.Active)
148+
d.Set("created_at", projectAccessToken.CreatedAt.String())
149+
d.Set("revoked", projectAccessToken.Revoked)
150+
d.Set("user_id", projectAccessToken.UserID)
151+
d.Set("scopes", projectAccessToken.Scopes)
152+
153+
return nil
154+
}
155+
}
156+
157+
page = response.NextPage
158+
}
159+
160+
log.Printf("[DEBUG] failed to read gitlab ProjectAccessToken %d, project ID %d", projectAccessTokenID, project)
161+
d.SetId("")
162+
return nil
163+
}
164+
165+
func resourceGitlabProjectAccessTokenDelete(d *schema.ResourceData, meta interface{}) error {
166+
client := meta.(*gitlab.Client)
167+
project := d.Get("project").(int)
168+
log.Printf("[DEBUG] Delete gitlab ProjectAccessToken %s", d.Id())
169+
170+
projectAccessTokenID, err := strconv.Atoi(d.Id())
171+
172+
if err != nil {
173+
return fmt.Errorf("%s cannot be converted to int", d.Id())
174+
}
175+
176+
_, err = client.ProjectAccessTokens.DeleteProjectAccessToken(project, projectAccessTokenID)
177+
return err
178+
}

0 commit comments

Comments
 (0)