Skip to content

Commit

Permalink
Merge pull request #588 from randomswdev/master
Browse files Browse the repository at this point in the history
Add the project access token
  • Loading branch information
nagyv committed Jan 27, 2022
2 parents 8bc5606 + c1fe559 commit a915ccb
Show file tree
Hide file tree
Showing 4 changed files with 545 additions and 0 deletions.
47 changes: 47 additions & 0 deletions docs/resources/project_access_token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# gitlab\_project\_access\_token

This resource allows you to create and manage Project Access Token for your GitLab projects.

## Example Usage

```hcl
resource "gitlab_project_access_token" "example" {
project = "25"
name = "Example project access token"
expires_at = "2020-03-14"
scopes = [ "api" ]
}
resource "gitlab_project_variable" "example" {
project = gitlab_project.example.id
key = "pat"
value = gitlab_project_access_token.example.token
}
```

## Argument Reference

The following arguments are supported:

* `project` - (Required, string) The id of the project to add the project access token to.

* `name` - (Required, string) A name to describe the project access token.

* `expires_at` - (Optional, string) Time the token will expire it, YYYY-MM-DD format. Will not expire per default.

* `scopes` - (Required, set of strings) Valid values: `api`, `read_api`, `read_repository`, `write_repository`.

## Attributes Reference

The following attributes are exported in addition to the arguments listed above:

* `token` - The secret token. This is only populated when creating a new project access token.

* `active` - True if the token is active.

* `created_at` - Time the token has been created, RFC3339 format.

* `revoked` - True if the token is revoked.

* `user_id` - The user_id associated to the token.
1 change: 1 addition & 0 deletions gitlab/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func Provider() *schema.Provider {
"gitlab_group_membership": resourceGitlabGroupMembership(),
"gitlab_project_variable": resourceGitlabProjectVariable(),
"gitlab_group_variable": resourceGitlabGroupVariable(),
"gitlab_project_access_token": resourceGitlabProjectAccessToken(),
"gitlab_project_cluster": resourceGitlabProjectCluster(),
"gitlab_service_slack": resourceGitlabServiceSlack(),
"gitlab_service_jira": resourceGitlabServiceJira(),
Expand Down
195 changes: 195 additions & 0 deletions gitlab/resource_gitlab_project_access_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package gitlab

import (
"context"
"fmt"
"log"
"strconv"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
gitlab "github.com/xanzy/go-gitlab"
)

func resourceGitlabProjectAccessToken() *schema.Resource {
// lintignore: XR002 // TODO: Resolve this tfproviderlint issue
return &schema.Resource{
CreateContext: resourceGitlabProjectAccessTokenCreate,
ReadContext: resourceGitlabProjectAccessTokenRead,
DeleteContext: resourceGitlabProjectAccessTokenDelete,

Schema: map[string]*schema.Schema{
"project": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"scopes": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"api", "read_api", "read_repository", "write_repository"}, false),
},
},
"expires_at": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: func(i interface{}, k string) (warnings []string, errors []error) {
v := i.(string)

if _, err := time.Parse("2006-01-02", v); err != nil {
errors = append(errors, fmt.Errorf("expected %q to be a valid YYYY-MM-DD date, got %q: %+v", k, i, err))
}

return warnings, errors
},
ForceNew: true,
},
"token": {
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
"active": {
Type: schema.TypeBool,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
"revoked": {
Type: schema.TypeBool,
Computed: true,
},
"user_id": {
Type: schema.TypeInt,
Computed: true,
},
},
}
}

func resourceGitlabProjectAccessTokenCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*gitlab.Client)

project := d.Get("project").(string)
options := &gitlab.CreateProjectAccessTokenOptions{
Name: gitlab.String(d.Get("name").(string)),
Scopes: *stringSetToStringSlice(d.Get("scopes").(*schema.Set)),
}

log.Printf("[DEBUG] create gitlab ProjectAccessToken %s %s for project ID %s", *options.Name, options.Scopes, project)

if v, ok := d.GetOk("expires_at"); ok {
parsedExpiresAt, err := time.Parse("2006-01-02", v.(string))
if err != nil {
return diag.Errorf("Invalid expires_at date: %v", err)
}
parsedExpiresAtISOTime := gitlab.ISOTime(parsedExpiresAt)
options.ExpiresAt = &parsedExpiresAtISOTime
log.Printf("[DEBUG] create gitlab ProjectAccessToken %s with expires_at %s for project ID %s", *options.Name, *options.ExpiresAt, project)
}

projectAccessToken, _, err := client.ProjectAccessTokens.CreateProjectAccessToken(project, options, gitlab.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

log.Printf("[DEBUG] created gitlab ProjectAccessToken %d - %s for project ID %s", projectAccessToken.ID, *options.Name, project)

PATstring := strconv.Itoa(projectAccessToken.ID)
d.SetId(buildTwoPartID(&project, &PATstring))
d.Set("token", projectAccessToken.Token)

return resourceGitlabProjectAccessTokenRead(ctx, d, meta)
}

func resourceGitlabProjectAccessTokenRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {

project, PATstring, err := parseTwoPartID(d.Id())
if err != nil {
return diag.Errorf("Error parsing ID: %s", d.Id())
}

client := meta.(*gitlab.Client)

projectAccessTokenID, err := strconv.Atoi(PATstring)
if err != nil {
return diag.Errorf("%s cannot be converted to int", PATstring)
}

log.Printf("[DEBUG] read gitlab ProjectAccessToken %d, project ID %s", projectAccessTokenID, project)

//there is a slight possibility to not find an existing item, for example
// 1. item is #101 (ie, in the 2nd page)
// 2. I load first page (ie. I don't find my target item)
// 3. A concurrent operation remove item 99 (ie, my target item shift to 1st page)
// 4. a concurrent operation add an item
// 5: I load 2nd page (ie. I don't find my target item)
// 6. Total pages and total items properties are unchanged (from the perspective of the reader)

page := 1
for page != 0 {
projectAccessTokens, response, err := client.ProjectAccessTokens.ListProjectAccessTokens(project, &gitlab.ListProjectAccessTokensOptions{Page: page, PerPage: 100}, gitlab.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

for _, projectAccessToken := range projectAccessTokens {
if projectAccessToken.ID == projectAccessTokenID {

d.Set("project", project)
d.Set("name", projectAccessToken.Name)
if projectAccessToken.ExpiresAt != nil {
d.Set("expires_at", projectAccessToken.ExpiresAt.String())
}
d.Set("active", projectAccessToken.Active)
d.Set("created_at", projectAccessToken.CreatedAt.String())
d.Set("revoked", projectAccessToken.Revoked)
d.Set("user_id", projectAccessToken.UserID)

err = d.Set("scopes", projectAccessToken.Scopes)
if err != nil {
return diag.FromErr(err)
}

return nil
}
}

page = response.NextPage
}

log.Printf("[DEBUG] failed to read gitlab ProjectAccessToken %d, project ID %s", projectAccessTokenID, project)
d.SetId("")
return nil
}

func resourceGitlabProjectAccessTokenDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {

project, PATstring, err := parseTwoPartID(d.Id())
if err != nil {
return diag.Errorf("Error parsing ID: %s", d.Id())
}

client := meta.(*gitlab.Client)

projectAccessTokenID, err := strconv.Atoi(PATstring)
if err != nil {
return diag.Errorf("%s cannot be converted to int", PATstring)
}

log.Printf("[DEBUG] Delete gitlab ProjectAccessToken %s", d.Id())
_, err = client.ProjectAccessTokens.DeleteProjectAccessToken(project, projectAccessTokenID, gitlab.WithContext(ctx))
return diag.FromErr(err)
}

0 comments on commit a915ccb

Please sign in to comment.