Add Github Organization provider.

Allows for managing organization membership, teams, team membership, and
team repositories.
This commit is contained in:
Jacob Severson 2016-03-08 23:05:22 +01:00 committed by John Engelman
parent 203171744f
commit c1b373ad5f
24 changed files with 1412 additions and 5 deletions

1
.gitignore vendored
View File

@ -20,5 +20,6 @@ website/node_modules
*~
.*.swp
.idea
*.iml
*.test
*.iml

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/github"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: github.Provider,
})
}

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,29 @@
package github
import (
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
type Config struct {
Token string
Organization string
}
type Organization struct {
name string
client *github.Client
}
// Client configures and returns a fully initialized GithubClient
func (c *Config) Client() (interface{}, error) {
var org Organization
org.name = c.Organization
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: c.Token},
)
tc := oauth2.NewClient(oauth2.NoContext, ts)
org.client = github.NewClient(tc)
return &org, nil
}

View File

@ -0,0 +1,56 @@
package github
import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
// The actual provider
return &schema.Provider{
Schema: map[string]*schema.Schema{
"token": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("GITHUB_TOKEN", nil),
Description: descriptions["token"],
},
"organization": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("GITHUB_ORGANIZATION", nil),
Description: descriptions["organization"],
},
},
ResourcesMap: map[string]*schema.Resource{
"github_team": resourceGithubTeam(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_membership": resourceGithubMembership(),
},
ConfigureFunc: providerConfigure,
}
}
var descriptions map[string]string
func init() {
descriptions = map[string]string{
"token": "The OAuth token used to connect to GitHub.",
"organization": "The GitHub organization name to manage.",
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := Config{
Token: d.Get("token").(string),
Organization: d.Get("organization").(string),
}
return config.Client()
}

View File

@ -0,0 +1,38 @@
package github
import (
"os"
"testing"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider
func init() {
testAccProvider = Provider().(*schema.Provider)
testAccProviders = map[string]terraform.ResourceProvider{
"github": testAccProvider,
}
}
func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvider_impl(t *testing.T) {
var _ terraform.ResourceProvider = Provider()
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("GITHUB_TOKEN"); v == "" {
t.Fatal("GITHUB_TOKEN must be set for acceptance tests")
}
if v := os.Getenv("GITHUB_ORGANIZATION"); v == "" {
t.Fatal("GITHUB_ORGANIZATION must be set for acceptance tests")
}
}

View File

@ -0,0 +1,85 @@
package github
import (
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceGithubMembership() *schema.Resource {
return &schema.Resource{
Create: resourceGithubMembershipCreate,
Read: resourceGithubMembershipRead,
Update: resourceGithubMembershipUpdate,
Delete: resourceGithubMembershipDelete,
Schema: map[string]*schema.Schema{
"username": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"role": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateRoleValueFunc([]string{"member", "admin"}),
Default: "member",
},
},
}
}
func resourceGithubMembershipCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
n := d.Get("username").(string)
r := d.Get("role").(string)
membership, _, err := client.Organizations.EditOrgMembership(n, meta.(*Organization).name,
&github.Membership{Role: &r})
if err != nil {
return err
}
d.SetId(buildTwoPartID(membership.Organization.Login, membership.User.Login))
return resourceGithubMembershipRead(d, meta)
}
func resourceGithubMembershipRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
membership, _, err := client.Organizations.GetOrgMembership(d.Get("username").(string), meta.(*Organization).name)
if err != nil {
d.SetId("")
return nil
}
username := membership.User.Login
roleName := membership.Role
d.Set("username", *username)
d.Set("role", *roleName)
return nil
}
func resourceGithubMembershipUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
n := d.Get("username").(string)
r := d.Get("role").(string)
_, _, err := client.Organizations.EditOrgMembership(n, meta.(*Organization).name, &github.Membership{
Role: &r,
})
if err != nil {
return err
}
return nil
}
func resourceGithubMembershipDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
n := d.Get("username").(string)
_, err := client.Organizations.RemoveOrgMembership(n, meta.(*Organization).name)
return err
}

View File

@ -0,0 +1,113 @@
package github
import (
"fmt"
"testing"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccGithubMembership_basic(t *testing.T) {
var membership github.Membership
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubMembershipDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGithubMembershipConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubMembershipExists("github_membership.test_org_membership", &membership),
testAccCheckGithubMembershipRoleState("github_membership.test_org_membership", &membership),
),
},
},
})
}
func testAccCheckGithubMembershipDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*Organization).client
for _, rs := range s.RootModule().Resources {
if rs.Type != "github_membership" {
continue
}
o, u := parseTwoPartID(rs.Primary.ID)
membership, resp, err := conn.Organizations.GetOrgMembership(u, o)
if err == nil {
if membership != nil &&
buildTwoPartID(membership.Organization.Login, membership.User.Login) == rs.Primary.ID {
return fmt.Errorf("Organization membership still exists")
}
}
if resp.StatusCode != 404 {
return err
}
return nil
}
return nil
}
func testAccCheckGithubMembershipExists(n string, membership *github.Membership) 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, u := parseTwoPartID(rs.Primary.ID)
githubMembership, _, err := conn.Organizations.GetOrgMembership(u, o)
if err != nil {
return err
}
*membership = *githubMembership
return nil
}
}
func testAccCheckGithubMembershipRoleState(n string, membership *github.Membership) 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, u := parseTwoPartID(rs.Primary.ID)
githubMembership, _, err := conn.Organizations.GetOrgMembership(u, o)
if err != nil {
return err
}
resourceRole := membership.Role
actualRole := githubMembership.Role
if *resourceRole != *actualRole {
return fmt.Errorf("Membership role %v in resource does match actual state of %v", *resourceRole, *actualRole)
}
return nil
}
}
const testAccGithubMembershipConfig = `
resource "github_membership" "test_org_membership" {
username = "TerraformDummyUser"
role = "member"
}
`

View File

@ -0,0 +1,90 @@
package github
import (
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceGithubTeam() *schema.Resource {
return &schema.Resource{
Create: resourceGithubTeamCreate,
Read: resourceGithubTeamRead,
Update: resourceGithubTeamUpdate,
Delete: resourceGithubTeamDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
}
}
func resourceGithubTeamCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
n := d.Get("name").(string)
desc := d.Get("description").(string)
githubTeam, _, err := client.Organizations.CreateTeam(meta.(*Organization).name, &github.Team{
Name: &n,
Description: &desc,
})
if err != nil {
return err
}
d.SetId(fromGithubID(githubTeam.ID))
return resourceGithubTeamRead(d, meta)
}
func resourceGithubTeamRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
team, err := getGithubTeam(d, client)
if err != nil {
d.SetId("")
return nil
}
d.Set("description", team.Description)
d.Set("name", team.Name)
return nil
}
func resourceGithubTeamUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
team, err := getGithubTeam(d, client)
if err != nil {
d.SetId("")
return nil
}
name := d.Get("name").(string)
description := d.Get("description").(string)
team.Description = &description
team.Name = &name
team, _, err = client.Organizations.EditTeam(*team.ID, team)
if err != nil {
return err
}
d.SetId(fromGithubID(team.ID))
return resourceGithubTeamRead(d, meta)
}
func resourceGithubTeamDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
id := toGithubID(d.Id())
_, err := client.Organizations.DeleteTeam(id)
return err
}
func getGithubTeam(d *schema.ResourceData, github *github.Client) (*github.Team, error) {
id := toGithubID(d.Id())
team, _, err := github.Organizations.GetTeam(id)
return team, err
}

View File

@ -0,0 +1,100 @@
package github
import (
"strings"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceGithubTeamMembership() *schema.Resource {
return &schema.Resource{
Create: resourceGithubTeamMembershipCreate,
Read: resourceGithubTeamMembershipRead,
// editing team memberships are not supported by github api so forcing new on any changes
Delete: resourceGithubTeamMembershipDelete,
Schema: map[string]*schema.Schema{
"team_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"username": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"role": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "member",
ValidateFunc: validateRoleValueFunc([]string{"member", "maintainer"}),
},
},
}
}
func resourceGithubTeamMembershipCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
n := d.Get("username").(string)
r := d.Get("role").(string)
_, _, err := client.Organizations.AddTeamMembership(toGithubID(t), n,
&github.OrganizationAddTeamMembershipOptions{Role: r})
if err != nil {
return err
}
d.SetId(buildTwoPartID(&t, &n))
return resourceGithubTeamMembershipRead(d, meta)
}
func resourceGithubTeamMembershipRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
n := d.Get("username").(string)
membership, _, err := client.Organizations.GetTeamMembership(toGithubID(t), n)
if err != nil {
d.SetId("")
return nil
}
team, user := getTeamAndUserFromURL(membership.URL)
d.Set("username", user)
d.Set("role", membership.Role)
d.Set("team_id", team)
return nil
}
func resourceGithubTeamMembershipDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
n := d.Get("username").(string)
_, err := client.Organizations.RemoveTeamMembership(toGithubID(t), n)
return err
}
func getTeamAndUserFromURL(url *string) (string, string) {
var team, user string
urlSlice := strings.Split(*url, "/")
for v := range urlSlice {
if urlSlice[v] == "teams" {
team = urlSlice[v+1]
}
if urlSlice[v] == "memberships" {
user = urlSlice[v+1]
}
}
return team, user
}

View File

@ -0,0 +1,152 @@
package github
import (
"fmt"
"testing"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccGithubTeamMembership_basic(t *testing.T) {
var membership github.Membership
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubTeamMembershipDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGithubTeamMembershipConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubTeamMembershipExists("github_team_membership.test_team_membership", &membership),
testAccCheckGithubTeamMembershipRoleState("github_team_membership.test_team_membership", "member", &membership),
),
},
resource.TestStep{
Config: testAccGithubTeamMembershipUpdateConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubTeamMembershipExists("github_team_membership.test_team_membership", &membership),
testAccCheckGithubTeamMembershipRoleState("github_team_membership.test_team_membership", "maintainer", &membership),
),
},
},
})
}
func testAccCheckGithubTeamMembershipDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*Organization).client
for _, rs := range s.RootModule().Resources {
if rs.Type != "github_team_membership" {
continue
}
t, u := parseTwoPartID(rs.Primary.ID)
membership, resp, err := conn.Organizations.GetTeamMembership(toGithubID(t), u)
if err == nil {
if membership != nil {
return fmt.Errorf("Team membership still exists")
}
}
if resp.StatusCode != 404 {
return err
}
return nil
}
return nil
}
func testAccCheckGithubTeamMembershipExists(n string, membership *github.Membership) 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 team membership ID is set")
}
conn := testAccProvider.Meta().(*Organization).client
t, u := parseTwoPartID(rs.Primary.ID)
teamMembership, _, err := conn.Organizations.GetTeamMembership(toGithubID(t), u)
if err != nil {
return err
}
*membership = *teamMembership
return nil
}
}
func testAccCheckGithubTeamMembershipRoleState(n, expected string, membership *github.Membership) 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 team membership ID is set")
}
conn := testAccProvider.Meta().(*Organization).client
t, u := parseTwoPartID(rs.Primary.ID)
teamMembership, _, err := conn.Organizations.GetTeamMembership(toGithubID(t), u)
if err != nil {
return err
}
resourceRole := membership.Role
actualRole := teamMembership.Role
if *resourceRole != expected {
return fmt.Errorf("Team membership role %v in resource does match expected state of %v", *resourceRole, expected)
}
if *resourceRole != *actualRole {
return fmt.Errorf("Team membership role %v in resource does match actual state of %v", *resourceRole, *actualRole)
}
return nil
}
}
const testAccGithubTeamMembershipConfig = `
resource "github_membership" "test_org_membership" {
username = "TerraformDummyUser"
role = "member"
}
resource "github_team" "test_team" {
name = "foo"
description = "Terraform acc test group"
}
resource "github_team_membership" "test_team_membership" {
team_id = "${github_team.test_team.id}"
username = "TerraformDummyUser"
role = "member"
}
`
const testAccGithubTeamMembershipUpdateConfig = `
resource "github_membership" "test_org_membership" {
username = "TerraformDummyUser"
role = "member"
}
resource "github_team" "test_team" {
name = "foo"
description = "Terraform acc test group"
}
resource "github_team_membership" "test_team_membership" {
team_id = "${github_team.test_team.id}"
username = "TerraformDummyUser"
role = "maintainer"
}
`

View File

@ -0,0 +1,129 @@
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,
Read: resourceGithubTeamRepositoryRead,
Update: resourceGithubTeamRepositoryUpdate,
Delete: resourceGithubTeamRepositoryDelete,
Schema: map[string]*schema.Schema{
"team_id": &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,
Default: "pull",
ValidateFunc: validateRoleValueFunc([]string{"pull", "push", "admin"}),
},
},
}
}
func resourceGithubTeamRepositoryCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
r := d.Get("repository").(string)
p := d.Get("permission").(string)
_, err := client.Organizations.AddTeamRepo(toGithubID(t), meta.(*Organization).name, r,
&github.OrganizationAddTeamRepoOptions{Permission: p})
if err != nil {
return err
}
d.SetId(buildTwoPartID(&t, &r))
return resourceGithubTeamRepositoryRead(d, meta)
}
func resourceGithubTeamRepositoryRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
r := d.Get("repository").(string)
repo, _, repoErr := client.Organizations.IsTeamRepo(toGithubID(t), meta.(*Organization).name, r)
if repoErr != nil {
d.SetId("")
return nil
}
repositoryName := repo.Name
d.Set("team_id", t)
d.Set("repository", repositoryName)
permName, permErr := getRepoPermission(repo.Permissions)
if permErr != nil {
return permErr
}
d.Set("permission", permName)
return nil
}
func resourceGithubTeamRepositoryUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
r := d.Get("repository").(string)
p := d.Get("permission").(string)
// the go-github library's AddTeamRepo method uses the add/update endpoint from Github API
_, err := client.Organizations.AddTeamRepo(toGithubID(t), meta.(*Organization).name, r,
&github.OrganizationAddTeamRepoOptions{Permission: p})
if err != nil {
return err
}
return resourceGithubTeamRepositoryRead(d, meta)
}
func resourceGithubTeamRepositoryDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Organization).client
t := d.Get("team_id").(string)
r := d.Get("repository").(string)
_, err := client.Organizations.RemoveTeamRepo(toGithubID(t), meta.(*Organization).name, r)
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

@ -0,0 +1,154 @@
package github
import (
"fmt"
"testing"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccGithubTeamRepository_basic(t *testing.T) {
var repository github.Repository
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubTeamRepositoryDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGithubTeamRepositoryConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubTeamRepositoryExists("github_team_repository.test_team_test_repo", &repository),
testAccCheckGithubTeamRepositoryRoleState("pull", &repository),
),
},
resource.TestStep{
Config: testAccGithubTeamRepositoryUpdateConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubTeamRepositoryExists("github_team_repository.test_team_test_repo", &repository),
testAccCheckGithubTeamRepositoryRoleState("push", &repository),
),
},
},
})
}
func TestAccCheckGetPermissions(t *testing.T) {
pullMap := map[string]bool{"pull": true, "push": false, "admin": false}
pushMap := map[string]bool{"pull": true, "push": true, "admin": false}
adminMap := map[string]bool{"pull": true, "push": true, "admin": true}
errorMap := map[string]bool{"pull": false, "push": false, "admin": false}
pull, _ := getRepoPermission(&pullMap)
if pull != "pull" {
t.Fatalf("Expected pull permission, actual: %s", pull)
}
push, _ := getRepoPermission(&pushMap)
if push != "push" {
t.Fatalf("Expected push permission, actual: %s", push)
}
admin, _ := getRepoPermission(&adminMap)
if admin != "admin" {
t.Fatalf("Expected admin permission, actual: %s", admin)
}
errPerm, err := getRepoPermission(&errorMap)
if err == nil {
t.Fatalf("Expected an error getting permissions, actual: %v", errPerm)
}
}
func testAccCheckGithubTeamRepositoryRoleState(role string, repository *github.Repository) resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceRole, err := getRepoPermission(repository.Permissions)
if err != nil {
return err
}
if resourceRole != role {
return fmt.Errorf("Team repository role %v in resource does match expected state of %v", resourceRole, role)
}
return nil
}
}
func testAccCheckGithubTeamRepositoryExists(n string, repository *github.Repository) 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 team repository ID is set")
}
conn := testAccProvider.Meta().(*Organization).client
t, r := parseTwoPartID(rs.Primary.ID)
repo, _, err := conn.Organizations.IsTeamRepo(toGithubID(t),
testAccProvider.Meta().(*Organization).name, r)
if err != nil {
return err
}
*repository = *repo
return nil
}
}
func testAccCheckGithubTeamRepositoryDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*Organization).client
for _, rs := range s.RootModule().Resources {
if rs.Type != "github_team_repository" {
continue
}
t, r := parseTwoPartID(rs.Primary.ID)
repo, resp, err := conn.Organizations.IsTeamRepo(toGithubID(t),
testAccProvider.Meta().(*Organization).name, r)
if err == nil {
if repo != nil &&
buildTwoPartID(&t, repo.Name) == rs.Primary.ID {
return fmt.Errorf("Team repository still exists")
}
}
if resp.StatusCode != 404 {
return err
}
return nil
}
return nil
}
const testAccGithubTeamRepositoryConfig = `
resource "github_team" "test_team" {
name = "foo"
description = "Terraform acc test group"
}
resource "github_team_repository" "test_team_test_repo" {
team_id = "${github_team.test_team.id}"
repository = "test-repo"
permission = "pull"
}
`
const testAccGithubTeamRepositoryUpdateConfig = `
resource "github_team" "test_team" {
name = "foo"
description = "Terraform acc test group"
}
resource "github_team_repository" "test_team_test_repo" {
team_id = "${github_team.test_team.id}"
repository = "test-repo"
permission = "push"
}
`

View File

@ -0,0 +1,108 @@
package github
import (
"fmt"
"testing"
"github.com/google/go-github/github"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccGithubTeam_basic(t *testing.T) {
var team github.Team
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubTeamDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccGithubTeamConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubTeamExists("github_team.foo", &team),
testAccCheckGithubTeamAttributes(&team, "foo", "Terraform acc test group"),
),
},
resource.TestStep{
Config: testAccGithubTeamUpdateConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckGithubTeamExists("github_team.foo", &team),
testAccCheckGithubTeamAttributes(&team, "foo2", "Terraform acc test group - updated"),
),
},
},
})
}
func testAccCheckGithubTeamExists(n string, team *github.Team) 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 Team ID is set")
}
conn := testAccProvider.Meta().(*Organization).client
githubTeam, _, err := conn.Organizations.GetTeam(toGithubID(rs.Primary.ID))
if err != nil {
return err
}
*team = *githubTeam
return nil
}
}
func testAccCheckGithubTeamAttributes(team *github.Team, name, description string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *team.Name != name {
return fmt.Errorf("Team name does not match: %s, %s", *team.Name, name)
}
if *team.Description != description {
return fmt.Errorf("Team description does not match: %s, %s", *team.Description, description)
}
return nil
}
}
func testAccCheckGithubTeamDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*Organization).client
for _, rs := range s.RootModule().Resources {
if rs.Type != "github_team" {
continue
}
team, resp, err := conn.Organizations.GetTeam(toGithubID(rs.Primary.ID))
if err == nil {
if team != nil &&
fromGithubID(team.ID) == rs.Primary.ID {
return fmt.Errorf("Team still exists")
}
}
if resp.StatusCode != 404 {
return err
}
return nil
}
return nil
}
const testAccGithubTeamConfig = `
resource "github_team" "foo" {
name = "foo"
description = "Terraform acc test group"
}
`
const testAccGithubTeamUpdateConfig = `
resource "github_team" "foo" {
name = "foo2"
description = "Terraform acc test group - updated"
}
`

View File

@ -0,0 +1,47 @@
package github
import (
"fmt"
"strconv"
"strings"
"github.com/hashicorp/terraform/helper/schema"
)
func toGithubID(id string) int {
githubID, _ := strconv.Atoi(id)
return githubID
}
func fromGithubID(id *int) string {
return strconv.Itoa(*id)
}
func validateRoleValueFunc(roles []string) schema.SchemaValidateFunc {
return func(v interface{}, k string) (we []string, errors []error) {
value := v.(string)
valid := false
for _, role := range roles {
if value == role {
valid = true
break
}
}
if !valid {
errors = append(errors, fmt.Errorf("%s is an invalid Github role type for %s", value, k))
}
return
}
}
// return the pieces of id `a:b` as a, b
func parseTwoPartID(id string) (string, string) {
parts := strings.SplitN(id, ":", 2)
return parts[0], parts[1]
}
// format the strings into an id `a:b`
func buildTwoPartID(a, b *string) string {
return fmt.Sprintf("%s:%s", *a, *b)
}

View File

@ -0,0 +1,55 @@
package github
import (
"testing"
)
func TestAccGithubUtilRole_validation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "invalid",
ErrCount: 1,
},
{
Value: "valid_one",
ErrCount: 0,
},
{
Value: "valid_two",
ErrCount: 0,
},
}
validationFunc := validateRoleValueFunc([]string{"valid_one", "valid_two"})
for _, tc := range cases {
_, errors := validationFunc(tc.Value, "github_membership")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected github_membership to trigger a validation error")
}
}
}
func TestAccGithubUtilTwoPartID(t *testing.T) {
partOne, partTwo := "foo", "bar"
id := buildTwoPartID(&partOne, &partTwo)
if id != "foo:bar" {
t.Fatalf("Expected two part id to be foo:bar, actual: %s", id)
}
parsedPartOne, parsedPartTwo := parseTwoPartID(id)
if parsedPartOne != "foo" {
t.Fatalf("Expected parsed part one foo, actual: %s", parsedPartOne)
}
if parsedPartTwo != "bar" {
t.Fatalf("Expected parsed part two bar, actual: %s", parsedPartTwo)
}
}

View File

@ -20,6 +20,7 @@ body.layout-dme,
body.layout-dnsimple,
body.layout-docker,
body.layout-dyn,
body.layout-github,
body.layout-google,
body.layout-heroku,
body.layout-mailgun,

View File

@ -0,0 +1,42 @@
---
layout: "github"
page_title: "Provider: Github"
sidebar_current: "docs-github-index"
description: |-
The Github provider is used to interact with Github organization resources.
---
# Github Provider
The Github provider is used to interact with Github organization resources.
The provider allows you to manage your Github organization's members and teams easily.
It needs to be configured with the proper credentials before it can be used.
Use the navigation to the left to read about the available resources.
## Example Usage
```
# Configure the Github Provider
provider "github" {
token = "${var.github_token}"
organization = "${var.github_organization}"
}
# Add a user to the organization
resource "github_membership" "membership_for_user_x" {
...
}
```
## Argument Reference
The following arguments are supported in the `provider` block:
* `token` - (Optional) This is the Github personal access token. It must be provided, but
it can also be sourced from the `GITHUB_TOKEN` environment variable.
* `organization` - (Optional) This is the target Github organization to manage. The account
corresponding to the token will need "owner" privileges for this organization. It must be provided, but
it can also be sourced from the `GITHUB_ORGANIZATION` environment variable.

View File

@ -0,0 +1,33 @@
---
layout: "github"
page_title: "Github: github_membership"
sidebar_current: "docs-github-resource-membership"
description: |-
Provides a Github membership resource.
---
# github\_membership
Provides a Github membership resource.
This resource allows you to add/remove users from your organization. When applied,
an invitation will be sent to the user to become part of the organization. When
destroyed, either the invitation will be cancelled or the user will be removed.
## Example Usage
```
# Add a user to the organization
resource "github_membership" "membership_for_some_user" {
username = "SomeUser"
role = "member"
}
```
## Argument Reference
The following arguments are supported:
* `username` - (Required) The user to add to the organization.
* `role` - (Optional) The role of the user within the organization.
Must be one of `member` or `admin`. Defaults to `member`.

View File

@ -0,0 +1,37 @@
---
layout: "github"
page_title: "Github: github_team"
sidebar_current: "docs-github-resource-team"
description: |-
Provides a Github team resource.
---
# github\_team
Provides a Github team resource.
This resource allows you to add/remove teams from your organization. When applied,
a new team will be created. When destroyed, that team will be removed.
## Example Usage
```
# Add a team to the organization
resource "github_team" "some_team" {
name = "some-team"
description = "Some cool team"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the team.
* `description` - (Optional) A description of the team.
## Attributes Reference
The following attributes are exported:
* `id` - The ID of the created team.

View File

@ -0,0 +1,46 @@
---
layout: "github"
page_title: "Github: github_team_membership"
sidebar_current: "docs-github-resource-team-membership"
description: |-
Provides a Github team membership resource.
---
# github\_team_membership
Provides a Github team membership resource.
This resource allows you to add/remove users from teams in your organization. When applied,
the user will be added to the team. If the user hasn't accepted their invitation to the
organization, they won't be part of the team until they do. When
destroyed, the user will be removed from the team.
## Example Usage
```
# Add a user to the organization
resource "github_membership" "membership_for_some_user" {
username = "SomeUser"
role = "member"
}
resource "github_team" "some_team" {
name = "SomeTeam"
description = "Some cool team"
}
resource "github_team_membership" "some_team_membership" {
team_id = "${github_team.some_team.id}"
username = "SomeUser"
role = "member"
}
```
## Argument Reference
The following arguments are supported:
* `team_id` - (Required) The Github team id
* `username` - (Required) The user to add to the team.
* `role` - (Optional) The role of the user within the team.
Must be one of `member` or `maintainer`. Defaults to `member`.

View File

@ -0,0 +1,39 @@
---
layout: "github"
page_title: "Github: github_team_repository"
sidebar_current: "docs-github-resource-team-repository"
description: |-
Provides a Github team repository resource.
---
# github\_team_repository
Provides a Github team repository resource.
This resource allows you to add/remove repositories from teams in your organization. When applied,
the repository will be added to the team. When destroyed, the repository will be removed from the team.
## Example Usage
```
# Add a repository to the team
resource "github_team" "some_team" {
name = "SomeTeam"
description = "Some cool team"
}
resource "github_team_repository" "some_team_repo" {
team_id = "${github_team.some_team.id}"
repository = "our-repo"
permission = "pull"
}
```
## Argument Reference
The following arguments are supported:
* `team_id` - (Required) The Github team id
* `repository` - (Required) The repository to add to the team.
* `permission` - (Optional) The permissions of team members regarding the repository.
Must be one of `pull`, `push`, or `admin`. Defaults to `pull`.

View File

@ -182,6 +182,10 @@
<a href="/docs/providers/dyn/index.html">Dyn</a>
</li>
<li<%= sidebar_current("docs-providers-github") %>>
<a href="/docs/providers/github/index.html">Github</a>
</li>
<li<%= sidebar_current("docs-providers-google") %>>
<a href="/docs/providers/google/index.html">Google Cloud</a>
</li>
@ -218,9 +222,9 @@
<a href="/docs/providers/rundeck/index.html">Rundeck</a>
</li>
<li<%= sidebar_current("docs-providers-statuscake") %>>
<a href="/docs/providers/statuscake/index.html">StatusCake</a>
</li>
<li<%= sidebar_current("docs-providers-statuscake") %>>
<a href="/docs/providers/statuscake/index.html">StatusCake</a>
</li>
<li<%= sidebar_current("docs-providers-template") %>>
<a href="/docs/providers/template/index.html">Template</a>
@ -238,9 +242,9 @@
<a href="/docs/providers/vcd/index.html">VMware vCloud Director</a>
</li>
<li<%= sidebar_current("docs-providers-vsphere") %>>
<li<%= sidebar_current("docs-providers-vsphere") %>>
<a href="/docs/providers/vsphere/index.html">VMware vSphere</a>
</li>
</li>
</ul>
</li>

View File

@ -0,0 +1,35 @@
<% wrap_layout :inner do %>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/providers/index.html">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-github-index") %>>
<a href="/docs/providers/github/index.html">Github Provider</a>
</li>
<li<%= sidebar_current(/^docs-github-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<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-team") %>>
<a href="/docs/providers/github/r/team.html">github_team</a>
</li>
<li<%= sidebar_current("docs-github-resource-team-membership") %>>
<a href="/docs/providers/github/r/team_membership.html">github_team_membership</a>
</li>
<li<%= sidebar_current("docs-github-resource-team-repository") %>>
<a href="/docs/providers/github/r/team_repository.html">github_team_repository</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>