Skip to content

Commit a915ccb

Browse files
authored
Merge pull request #588 from randomswdev/master
Add the project access token
2 parents 8bc5606 + c1fe559 commit a915ccb

File tree

4 files changed

+545
-0
lines changed

4 files changed

+545
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# gitlab\_project\_access\_token
2+
3+
This resource allows you to create and manage Project Access Token for your GitLab projects.
4+
5+
## Example Usage
6+
7+
```hcl
8+
resource "gitlab_project_access_token" "example" {
9+
project = "25"
10+
name = "Example project access token"
11+
expires_at = "2020-03-14"
12+
13+
scopes = [ "api" ]
14+
}
15+
16+
resource "gitlab_project_variable" "example" {
17+
project = gitlab_project.example.id
18+
key = "pat"
19+
value = gitlab_project_access_token.example.token
20+
}
21+
```
22+
23+
## Argument Reference
24+
25+
The following arguments are supported:
26+
27+
* `project` - (Required, string) The id of the project to add the project access token to.
28+
29+
* `name` - (Required, string) A name to describe the project access token.
30+
31+
* `expires_at` - (Optional, string) Time the token will expire it, YYYY-MM-DD format. Will not expire per default.
32+
33+
* `scopes` - (Required, set of strings) Valid values: `api`, `read_api`, `read_repository`, `write_repository`.
34+
35+
## Attributes Reference
36+
37+
The following attributes are exported in addition to the arguments listed above:
38+
39+
* `token` - The secret token. This is only populated when creating a new project access token.
40+
41+
* `active` - True if the token is active.
42+
43+
* `created_at` - Time the token has been created, RFC3339 format.
44+
45+
* `revoked` - True if the token is revoked.
46+
47+
* `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
@@ -90,6 +90,7 @@ func Provider() *schema.Provider {
9090
"gitlab_group_membership": resourceGitlabGroupMembership(),
9191
"gitlab_project_variable": resourceGitlabProjectVariable(),
9292
"gitlab_group_variable": resourceGitlabGroupVariable(),
93+
"gitlab_project_access_token": resourceGitlabProjectAccessToken(),
9394
"gitlab_project_cluster": resourceGitlabProjectCluster(),
9495
"gitlab_service_slack": resourceGitlabServiceSlack(),
9596
"gitlab_service_jira": resourceGitlabServiceJira(),
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package gitlab
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"strconv"
8+
"time"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
13+
gitlab "github.com/xanzy/go-gitlab"
14+
)
15+
16+
func resourceGitlabProjectAccessToken() *schema.Resource {
17+
// lintignore: XR002 // TODO: Resolve this tfproviderlint issue
18+
return &schema.Resource{
19+
CreateContext: resourceGitlabProjectAccessTokenCreate,
20+
ReadContext: resourceGitlabProjectAccessTokenRead,
21+
DeleteContext: resourceGitlabProjectAccessTokenDelete,
22+
23+
Schema: map[string]*schema.Schema{
24+
"project": {
25+
Type: schema.TypeString,
26+
Required: true,
27+
ForceNew: true,
28+
},
29+
"name": {
30+
Type: schema.TypeString,
31+
Required: true,
32+
ForceNew: true,
33+
},
34+
"scopes": {
35+
Type: schema.TypeSet,
36+
Required: true,
37+
ForceNew: true,
38+
Elem: &schema.Schema{
39+
Type: schema.TypeString,
40+
ValidateFunc: validation.StringInSlice([]string{"api", "read_api", "read_repository", "write_repository"}, false),
41+
},
42+
},
43+
"expires_at": {
44+
Type: schema.TypeString,
45+
Optional: true,
46+
ValidateFunc: func(i interface{}, k string) (warnings []string, errors []error) {
47+
v := i.(string)
48+
49+
if _, err := time.Parse("2006-01-02", v); err != nil {
50+
errors = append(errors, fmt.Errorf("expected %q to be a valid YYYY-MM-DD date, got %q: %+v", k, i, err))
51+
}
52+
53+
return warnings, errors
54+
},
55+
ForceNew: true,
56+
},
57+
"token": {
58+
Type: schema.TypeString,
59+
Computed: true,
60+
Sensitive: true,
61+
},
62+
"active": {
63+
Type: schema.TypeBool,
64+
Computed: true,
65+
},
66+
"created_at": {
67+
Type: schema.TypeString,
68+
Computed: true,
69+
},
70+
"revoked": {
71+
Type: schema.TypeBool,
72+
Computed: true,
73+
},
74+
"user_id": {
75+
Type: schema.TypeInt,
76+
Computed: true,
77+
},
78+
},
79+
}
80+
}
81+
82+
func resourceGitlabProjectAccessTokenCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
83+
client := meta.(*gitlab.Client)
84+
85+
project := d.Get("project").(string)
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 for project ID %s", *options.Name, options.Scopes, project)
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 diag.Errorf("Invalid expires_at date: %v", err)
97+
}
98+
parsedExpiresAtISOTime := gitlab.ISOTime(parsedExpiresAt)
99+
options.ExpiresAt = &parsedExpiresAtISOTime
100+
log.Printf("[DEBUG] create gitlab ProjectAccessToken %s with expires_at %s for project ID %s", *options.Name, *options.ExpiresAt, project)
101+
}
102+
103+
projectAccessToken, _, err := client.ProjectAccessTokens.CreateProjectAccessToken(project, options, gitlab.WithContext(ctx))
104+
if err != nil {
105+
return diag.FromErr(err)
106+
}
107+
108+
log.Printf("[DEBUG] created gitlab ProjectAccessToken %d - %s for project ID %s", projectAccessToken.ID, *options.Name, project)
109+
110+
PATstring := strconv.Itoa(projectAccessToken.ID)
111+
d.SetId(buildTwoPartID(&project, &PATstring))
112+
d.Set("token", projectAccessToken.Token)
113+
114+
return resourceGitlabProjectAccessTokenRead(ctx, d, meta)
115+
}
116+
117+
func resourceGitlabProjectAccessTokenRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
118+
119+
project, PATstring, err := parseTwoPartID(d.Id())
120+
if err != nil {
121+
return diag.Errorf("Error parsing ID: %s", d.Id())
122+
}
123+
124+
client := meta.(*gitlab.Client)
125+
126+
projectAccessTokenID, err := strconv.Atoi(PATstring)
127+
if err != nil {
128+
return diag.Errorf("%s cannot be converted to int", PATstring)
129+
}
130+
131+
log.Printf("[DEBUG] read gitlab ProjectAccessToken %d, project ID %s", projectAccessTokenID, project)
132+
133+
//there is a slight possibility to not find an existing item, for example
134+
// 1. item is #101 (ie, in the 2nd page)
135+
// 2. I load first page (ie. I don't find my target item)
136+
// 3. A concurrent operation remove item 99 (ie, my target item shift to 1st page)
137+
// 4. a concurrent operation add an item
138+
// 5: I load 2nd page (ie. I don't find my target item)
139+
// 6. Total pages and total items properties are unchanged (from the perspective of the reader)
140+
141+
page := 1
142+
for page != 0 {
143+
projectAccessTokens, response, err := client.ProjectAccessTokens.ListProjectAccessTokens(project, &gitlab.ListProjectAccessTokensOptions{Page: page, PerPage: 100}, gitlab.WithContext(ctx))
144+
if err != nil {
145+
return diag.FromErr(err)
146+
}
147+
148+
for _, projectAccessToken := range projectAccessTokens {
149+
if projectAccessToken.ID == projectAccessTokenID {
150+
151+
d.Set("project", project)
152+
d.Set("name", projectAccessToken.Name)
153+
if projectAccessToken.ExpiresAt != nil {
154+
d.Set("expires_at", projectAccessToken.ExpiresAt.String())
155+
}
156+
d.Set("active", projectAccessToken.Active)
157+
d.Set("created_at", projectAccessToken.CreatedAt.String())
158+
d.Set("revoked", projectAccessToken.Revoked)
159+
d.Set("user_id", projectAccessToken.UserID)
160+
161+
err = d.Set("scopes", projectAccessToken.Scopes)
162+
if err != nil {
163+
return diag.FromErr(err)
164+
}
165+
166+
return nil
167+
}
168+
}
169+
170+
page = response.NextPage
171+
}
172+
173+
log.Printf("[DEBUG] failed to read gitlab ProjectAccessToken %d, project ID %s", projectAccessTokenID, project)
174+
d.SetId("")
175+
return nil
176+
}
177+
178+
func resourceGitlabProjectAccessTokenDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
179+
180+
project, PATstring, err := parseTwoPartID(d.Id())
181+
if err != nil {
182+
return diag.Errorf("Error parsing ID: %s", d.Id())
183+
}
184+
185+
client := meta.(*gitlab.Client)
186+
187+
projectAccessTokenID, err := strconv.Atoi(PATstring)
188+
if err != nil {
189+
return diag.Errorf("%s cannot be converted to int", PATstring)
190+
}
191+
192+
log.Printf("[DEBUG] Delete gitlab ProjectAccessToken %s", d.Id())
193+
_, err = client.ProjectAccessTokens.DeleteProjectAccessToken(project, projectAccessTokenID, gitlab.WithContext(ctx))
194+
return diag.FromErr(err)
195+
}

0 commit comments

Comments
 (0)