Skip to content

Commit

Permalink
Add GitHub organization provider.
Browse files Browse the repository at this point in the history
Allows for managing organization membership, organization teams, team
membership, and team repositories.
  • Loading branch information
JacobASeverson authored and johnrengelman committed Feb 22, 2016
1 parent fc519b8 commit d9e59a6
Show file tree
Hide file tree
Showing 79 changed files with 10,633 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ website/node_modules
*~
.*.swp
.idea
*.iml
*.test
*.iml
6 changes: 6 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions builtin/bins/provider-github/main.go
Original file line number Diff line number Diff line change
@@ -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,
})
}
1 change: 1 addition & 0 deletions builtin/bins/provider-github/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package main
29 changes: 29 additions & 0 deletions builtin/providers/github/config.go
Original file line number Diff line number Diff line change
@@ -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
}
56 changes: 56 additions & 0 deletions builtin/providers/github/provider.go
Original file line number Diff line number Diff line change
@@ -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()
}
38 changes: 38 additions & 0 deletions builtin/providers/github/provider_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
85 changes: 85 additions & 0 deletions builtin/providers/github/resource_github_membership.go
Original file line number Diff line number Diff line change
@@ -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
}
113 changes: 113 additions & 0 deletions builtin/providers/github/resource_github_membership_test.go
Original file line number Diff line number Diff line change
@@ -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"
}
`
Loading

0 comments on commit d9e59a6

Please sign in to comment.