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, teams, team membership, and
team repositories.
  • Loading branch information
JacobASeverson authored and johnrengelman committed Mar 8, 2016
1 parent 2031717 commit c1b373a
Show file tree
Hide file tree
Showing 24 changed files with 1,412 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
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 c1b373a

Please sign in to comment.