+github_repository_collaborator (#6861)

This commit is contained in:
Michael Handler 2016-06-01 17:06:35 -07:00 committed by Paul Stack
parent 40d0c5f635
commit a1e92c6f77
9 changed files with 322 additions and 33 deletions

View File

@ -32,10 +32,11 @@ func Provider() terraform.ResourceProvider {
},
ResourcesMap: map[string]*schema.Resource{
"github_team": resourceGithubTeam(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_membership": resourceGithubMembership(),
"github_team": resourceGithubTeam(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_membership": resourceGithubMembership(),
"github_repository_collaborator": resourceGithubRepositoryCollaborator(),
},
ConfigureFunc: providerConfigure,

View File

@ -8,6 +8,8 @@ import (
"github.com/hashicorp/terraform/terraform"
)
const testRepo string = "test-repo"
var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider
@ -38,4 +40,7 @@ func testAccPreCheck(t *testing.T) {
if v := os.Getenv("GITHUB_TEST_USER"); v == "" {
t.Fatal("GITHUB_TEST_USER must be set for acceptance tests")
}
if v := os.Getenv("GITHUB_TEST_COLLABORATOR"); v == "" {
t.Fatal("GITHUB_TEST_COLLABORATOR must be set for acceptance tests")
}
}

View File

@ -0,0 +1,99 @@
package github
import (
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceGithubRepositoryCollaborator() *schema.Resource {
return &schema.Resource{
Create: resourceGithubRepositoryCollaboratorCreate,
Read: resourceGithubRepositoryCollaboratorRead,
// editing repository collaborators are not supported by github api so forcing new on any changes
Delete: resourceGithubRepositoryCollaboratorDelete,
Schema: map[string]*schema.Schema{
"username": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"repository": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"permission": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "push",
ValidateFunc: validateValueFunc([]string{"pull", "push", "admin"}),
},
},
}
}
func resourceGithubRepositoryCollaboratorCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
u := d.Get("username").(string)
r := d.Get("repository").(string)
p := d.Get("permission").(string)
_, err := client.Repositories.AddCollaborator(meta.(*Organization).name, r, u,
&github.RepositoryAddCollaboratorOptions{Permission: p})
if err != nil {
return err
}
d.SetId(buildTwoPartID(&r, &u))
return resourceGithubRepositoryCollaboratorRead(d, meta)
}
func resourceGithubRepositoryCollaboratorRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
u := d.Get("username").(string)
r := d.Get("repository").(string)
isCollaborator, _, err := client.Repositories.IsCollaborator(meta.(*Organization).name, r, u)
if !isCollaborator || err != nil {
d.SetId("")
return nil
}
collaborators, _, err := client.Repositories.ListCollaborators(meta.(*Organization).name, r,
&github.ListOptions{})
if err != nil {
return err
}
for _, c := range collaborators {
if *c.Login == u {
permName, err := getRepoPermission(c.Permissions)
if err != nil {
return err
}
d.Set("permission", permName)
return nil
}
}
return nil
}
func resourceGithubRepositoryCollaboratorDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
u := d.Get("username").(string)
r := d.Get("repository").(string)
_, err := client.Repositories.RemoveCollaborator(meta.(*Organization).name, r, u)
return err
}

View File

@ -0,0 +1,135 @@
package github
import (
"fmt"
"os"
"testing"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
const expectedPermission string = "admin"
func TestAccGithubRepositoryCollaborator_basic(t *testing.T) {
testCollaborator := os.Getenv("GITHUB_TEST_COLLABORATOR")
testAccGithubRepositoryCollaboratorConfig := fmt.Sprintf(`
resource "github_repository_collaborator" "test_repo_collaborator" {
repository = "%s"
username = "%s"
permission = "%s"
}
`, testRepo, testCollaborator, expectedPermission)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubRepositoryCollaboratorDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGithubRepositoryCollaboratorConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubRepositoryCollaboratorExists("github_repository_collaborator.test_repo_collaborator"),
testAccCheckGithubRepositoryCollaboratorPermission("github_repository_collaborator.test_repo_collaborator"),
),
},
},
})
}
func testAccCheckGithubRepositoryCollaboratorDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*Organization).client
for _, rs := range s.RootModule().Resources {
if rs.Type != "github_repository_collaborator" {
continue
}
o := testAccProvider.Meta().(*Organization).name
r, u := parseTwoPartID(rs.Primary.ID)
isCollaborator, _, err := conn.Repositories.IsCollaborator(o, r, u)
if err != nil {
return err
}
if isCollaborator {
return fmt.Errorf("Repository collaborator still exists")
}
return nil
}
return nil
}
func testAccCheckGithubRepositoryCollaboratorExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not Found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No membership ID is set")
}
conn := testAccProvider.Meta().(*Organization).client
o := testAccProvider.Meta().(*Organization).name
r, u := parseTwoPartID(rs.Primary.ID)
isCollaborator, _, err := conn.Repositories.IsCollaborator(o, r, u)
if err != nil {
return err
}
if !isCollaborator {
return fmt.Errorf("Repository collaborator does not exist")
}
return nil
}
}
func testAccCheckGithubRepositoryCollaboratorPermission(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not Found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No membership ID is set")
}
conn := testAccProvider.Meta().(*Organization).client
o := testAccProvider.Meta().(*Organization).name
r, u := parseTwoPartID(rs.Primary.ID)
collaborators, _, err := conn.Repositories.ListCollaborators(o, r, &github.ListOptions{})
if err != nil {
return err
}
for _, c := range collaborators {
if *c.Login == u {
permName, err := getRepoPermission(c.Permissions)
if err != nil {
return err
}
if permName != expectedPermission {
return fmt.Errorf("Expected permission %s on repository collaborator, actual permission %s", expectedPermission, permName)
}
return nil
}
}
return fmt.Errorf("Repository collaborator did not appear in list of collaborators on repository")
}
}

View File

@ -1,16 +1,10 @@
package github
import (
"errors"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/schema"
)
const pullPermission string = "pull"
const pushPermission string = "push"
const adminPermission string = "admin"
func resourceGithubTeamRepository() *schema.Resource {
return &schema.Resource{
Create: resourceGithubTeamRepositoryCreate,
@ -110,20 +104,3 @@ func resourceGithubTeamRepositoryDelete(d *schema.ResourceData, meta interface{}
return err
}
func getRepoPermission(p *map[string]bool) (string, error) {
// Permissions are returned in this map format such that if you have a certain level
// of permission, all levels below are also true. For example, if a team has push
// permission, the map will be: {"pull": true, "push": true, "admin": false}
if (*p)[adminPermission] {
return adminPermission, nil
} else if (*p)[pushPermission] {
return pushPermission, nil
} else {
if (*p)[pullPermission] {
return pullPermission, nil
}
return "", errors.New("At least one permission expected from permissions map.")
}
}

View File

@ -127,7 +127,7 @@ func testAccCheckGithubTeamRepositoryDestroy(s *terraform.State) error {
return nil
}
const testAccGithubTeamRepositoryConfig = `
var testAccGithubTeamRepositoryConfig string = fmt.Sprintf(`
resource "github_team" "test_team" {
name = "foo"
description = "Terraform acc test group"
@ -135,12 +135,12 @@ resource "github_team" "test_team" {
resource "github_team_repository" "test_team_test_repo" {
team_id = "${github_team.test_team.id}"
repository = "test-repo"
repository = "%s"
permission = "pull"
}
`
`, testRepo)
const testAccGithubTeamRepositoryUpdateConfig = `
var testAccGithubTeamRepositoryUpdateConfig string = fmt.Sprintf(`
resource "github_team" "test_team" {
name = "foo"
description = "Terraform acc test group"
@ -148,7 +148,7 @@ resource "github_team" "test_team" {
resource "github_team_repository" "test_team_test_repo" {
team_id = "${github_team.test_team.id}"
repository = "test-repo"
repository = "%s"
permission = "push"
}
`
`, testRepo)

View File

@ -0,0 +1,24 @@
package github
import "errors"
const pullPermission string = "pull"
const pushPermission string = "push"
const adminPermission string = "admin"
func getRepoPermission(p *map[string]bool) (string, error) {
// Permissions are returned in this map format such that if you have a certain level
// of permission, all levels below are also true. For example, if a team has push
// permission, the map will be: {"pull": true, "push": true, "admin": false}
if (*p)[adminPermission] {
return adminPermission, nil
} else if (*p)[pushPermission] {
return pushPermission, nil
} else {
if (*p)[pullPermission] {
return pullPermission, nil
}
return "", errors.New("At least one permission expected from permissions map.")
}
}

View File

@ -0,0 +1,45 @@
---
layout: "github"
page_title: "GitHub: github_repository_collaborator"
sidebar_current: "docs-github-resource-repository-collaborator"
description: |-
Provides a GitHub repository collaborator resource.
---
# github\_repository_collaborator
Provides a GitHub repository collaborator resource.
This resource allows you to add/remove collaborators from repositories in your
organization. Collaborators can have explicit (and differing levels of) read,
write, or administrator access to specific repositories in your organization,
without giving the user full organization membership.
When applied, an invitation will be sent to the user to become a collaborator
on a repository. When destroyed, either the invitation will be cancelled or the
collaborator will be removed from the repository.
Further documentation on GitHub collaborators:
- [Adding outside collaborators to repositories in your organization](https://help.github.com/articles/adding-outside-collaborators-to-repositories-in-your-organization/)
- [Converting an organization member to an outside collaborator](https://help.github.com/articles/converting-an-organization-member-to-an-outside-collaborator/)
## Example Usage
```
# Add a collaborator to a repository
resource "github_repository_collaborator" "a_repo_collaborator" {
repository = "our-cool-repo"
username = "SomeUser"
permission = "admin"
}
```
## Argument Reference
The following arguments are supported:
* `repository` - (Required) The GitHub repository
* `username` - (Required) The user to add to the repository as a collaborator.
* `permission` - (Optional) The permission of the outside collaborator for the repository.
Must be one of `pull`, `push`, or `admin`. Defaults to `push`.

View File

@ -16,6 +16,9 @@
<li<%= sidebar_current("docs-github-resource-membership") %>>
<a href="/docs/providers/github/r/membership.html">github_membership</a>
</li>
<li<%= sidebar_current("docs-github-resource-repository-collaborator") %>>
<a href="/docs/providers/github/r/repository_collaborator.html">github_repository_collaborator</a>
</li>
<li<%= sidebar_current("docs-github-resource-team") %>>
<a href="/docs/providers/github/r/team.html">github_team</a>
</li>