-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #588 from randomswdev/master
Add the project access token
- Loading branch information
Showing
4 changed files
with
545 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.