Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kayrus optional parameters #255

Merged
merged 9 commits into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions github/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Config struct {
BaseURL string
Insecure bool
Individual bool
Anonymous bool
}

type Organization struct {
Expand All @@ -29,10 +30,8 @@ type Organization struct {
// Client configures and returns a fully initialized GithubClient
func (c *Config) Client() (interface{}, error) {
var org Organization

ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: c.Token},
)
var ts oauth2.TokenSource
var tc *http.Client

ctx := context.Background()

Expand All @@ -41,23 +40,47 @@ func (c *Config) Client() (interface{}, error) {
ctx = context.WithValue(ctx, oauth2.HTTPClient, insecureClient)
}

// Either Organization needs to be set, or Individual needs to be true
if c.Organization != "" && c.Individual {
return nil, fmt.Errorf("If `individual` is true, `organization` cannot be set.")
}
if c.Organization == "" && !c.Individual {
return nil, fmt.Errorf("If `individual` is false, `organization` is required.")
}

if c.Individual {
org.name = ""
} else if c.Organization != "" {
org.name = c.Organization
} else {
return nil, fmt.Errorf("If `individual` is false, `organization` is required.")
org.name = c.Organization
}

tc := oauth2.NewClient(ctx, ts)
// Either run as anonymous, or run with a Token
if c.Token != "" && c.Anonymous {
return nil, fmt.Errorf("If `anonymous` is true, `token` cannot be set.")
}
if c.Token == "" && !c.Anonymous {
return nil, fmt.Errorf("If `anonymous` is false, `token` is required.")
}

if !c.Anonymous {
ts = oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: c.Token},
)
}

tc.Transport = NewEtagTransport(tc.Transport)
tc = oauth2.NewClient(ctx, ts)

tc.Transport = NewRateLimitTransport(tc.Transport)
if c.Anonymous {
tc.Transport = http.DefaultTransport
} else {
tc.Transport = NewEtagTransport(tc.Transport)
}

tc.Transport = NewRateLimitTransport(tc.Transport)
tc.Transport = logging.NewTransport("Github", tc.Transport)

org.client = github.NewClient(tc)

if c.BaseURL != "" {
u, err := url.Parse(c.BaseURL)
if err != nil {
Expand Down
21 changes: 17 additions & 4 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func Provider() terraform.ResourceProvider {
Schema: map[string]*schema.Schema{
"token": {
Type: schema.TypeString,
Required: true,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("GITHUB_TOKEN", nil),
Description: descriptions["token"],
},
Expand Down Expand Up @@ -39,6 +39,11 @@ func Provider() terraform.ResourceProvider {
Default: false,
Description: descriptions["individual"],
},
"anonymous": {
Type: schema.TypeBool,
Optional: true,
Description: descriptions["anonymous"],
},
},

ResourcesMap: map[string]*schema.Resource{
Expand Down Expand Up @@ -81,17 +86,24 @@ var descriptions map[string]string

func init() {
descriptions = map[string]string{
"token": "The OAuth token used to connect to GitHub.",
"token": "The OAuth token used to connect to GitHub. " +
"If `anonymous` is false, `token` is required.",

"organization": "The GitHub organization name to manage. " +
"If `individual` is false, organization is required.",
"If `individual` is false, `organization` is required.",

"base_url": "The GitHub Base API URL",

"insecure": "Whether server should be accessed " +
"without verifying the TLS certificate.",

"individual": "Whether to run outside an organization.",
"individual": "Run outside an organization. When `individual`" +
"is true, the provider will run outside the scope of an" +
"organization.",

"anonymous": "Authenticate without a token. When `anonymous`" +
"is true, the provider will not be able to access resources" +
"that require authentication.",
}
}

Expand All @@ -103,6 +115,7 @@ func providerConfigure(p *schema.Provider) schema.ConfigureFunc {
BaseURL: d.Get("base_url").(string),
Insecure: d.Get("insecure").(bool),
Individual: d.Get("individual").(bool),
Anonymous: d.Get("anonymous").(bool),
}

meta, err := config.Client()
Expand Down
78 changes: 71 additions & 7 deletions github/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"testing"

"github.com/hashicorp/terraform/helper/resource"
Expand Down Expand Up @@ -66,11 +67,7 @@ func testAccPreCheck(t *testing.T) {
}

func TestProvider_individual(t *testing.T) {
individualProviderConfig := `provider "github" {
organization = ""
individual = true
}
`

username := "hashibot"
resource.Test(t, resource.TestCase{
PreCheck: func() {
Expand All @@ -80,16 +77,65 @@ func TestProvider_individual(t *testing.T) {
CheckDestroy: testAccCheckGithubMembershipDestroy,
Steps: []resource.TestStep{
{
Config: individualProviderConfig + testAccCheckGithubUserDataSourceConfig(username),
// Test individual is true. Because GITHUB_ORGANIZATION should be set for these tests, we'll pass an
// empty string for `org` to unset the organization
Config: configProviderOrganization("", true) + testAccCheckGithubUserDataSourceConfig(username),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.github_user.test", "name"),
resource.TestCheckResourceAttr("data.github_user.test", "name", "HashiBot"),
),
},
{
Config: individualProviderConfig + testAccGithubMembershipConfig(username),
// Test individual is true, but resource requires organization. Because GITHUB_ORGANIZATION should be
// set for these tests, we'll pass an empty string for `org` to unset the organization
Config: configProviderOrganization("", true) + testAccGithubMembershipConfig(username),
ExpectError: regexp.MustCompile("This resource requires GitHub organization to be set on the provider."),
},
{
// Test conflicting `individual` and `organization`
Config: configProviderOrganization(testOrganization, true) + testAccCheckGithubUserDataSourceConfig(username),
ExpectError: regexp.MustCompile("If `individual` is true, `organization` cannot be set."),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this test pass? conflictingProviderConfig, from what I can see, only sets individual to true, it doesn't set an organization.

},
{
// Test neither `individual` or `organization` is set. Because GITHUB_ORGANIZATION should be
// set for these tests, we'll pass an empty string for `org` to unset the organization
Config: configProviderOrganization("", false) + testAccCheckGithubUserDataSourceConfig(username),
ExpectError: regexp.MustCompile("If `individual` is false, `organization` is required."),
},
},
})
}

func TestProvider_anonymous(t *testing.T) {

username := "hashibot"
resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckGithubMembershipDestroy,
Steps: []resource.TestStep{
{
// Test anonymous is true. Because GITHUB_TOKEN should be set for these tests, we'll pass an
// empty string for `token` to unset the token
Config: configProviderToken("", true) + testAccCheckGithubUserDataSourceConfig(username),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.github_user.test", "name"),
resource.TestCheckResourceAttr("data.github_user.test", "name", "HashiBot"),
),
},
{
// Test conflicting `anonymous` and `token`
Config: configProviderToken(os.Getenv("GITHUB_TOKEN"), true) + testAccCheckGithubUserDataSourceConfig(username),
ExpectError: regexp.MustCompile("If `anonymous` is true, `token` cannot be set."),
},
{
// Test neither `anonymous` or `token` is set. Because GITHUB_TOKEN should be
// set for these tests, we'll pass an empty string for `token` to unset the token
Config: configProviderToken("", false) + testAccCheckGithubUserDataSourceConfig(username),
ExpectError: regexp.MustCompile("If `anonymous` is false, `token` is required."),
},
},
})
}
Expand Down Expand Up @@ -162,6 +208,24 @@ func testRespondJson(responseBody string) func(http.ResponseWriter, *http.Reques
}
}

func configProviderOrganization(org string, individual bool) string {
return fmt.Sprintf(`
provider "github" {
organization = "%s"
individual = %s
}
`, org, strconv.FormatBool(individual))
}

func configProviderToken(token string, anonymous bool) string {
return fmt.Sprintf(`
provider "github" {
token = "%s"
anonymous = %s
}
`, token, strconv.FormatBool(anonymous))
}

const userResponseBody = `{
"login": "hashibot",
"id": 1,
Expand Down
19 changes: 13 additions & 6 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ resource "github_membership" "membership_for_user_x" {

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.
* `token` - (Optional) This is the GitHub personal access token. It can also be
sourced from the `GITHUB_TOKEN` environment variable. If `anonymous` is false,
token is required.

* `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.
* `organization` - (Optional) This is the target GitHub organization to manage.
The account corresponding to the token will need "owner" privileges for this
organization. It can also be sourced from the `GITHUB_ORGANIZATION`
environment variable. If `individual` is false, organization is required.

* `base_url` - (Optional) This is the target GitHub base API endpoint. Providing a value is a
requirement when working with GitHub Enterprise. It is optional to provide this value and
Expand All @@ -53,4 +55,9 @@ The following arguments are supported in the `provider` block:
Such trusted certificate *does not require* this option to be enabled.
Defaults to `false`.

* `individual`: (Optional) Whether to run outside an organization.
* `individual`: (Optional) Run outside an organization. When `individual` is true, the provider will run outside
the scope of an organization. Defaults to `false`.

* `anonymous`: (Optional) Authenticate without a token. When `anonymous` is true, the provider will not be able to
access resources that require authentication. Setting to true will lead the GitHub provider to work in an anonymous
mode with the corresponding API [rate limits](https://developer.github.com/v3/#rate-limiting). Defaults to `false`.