Skip to content

Commit

Permalink
Set User-Agent header in client (#256)
Browse files Browse the repository at this point in the history
* Sketch user-agent setup

* Cleanup config and schema setup

* Wrap errors in client

* Use ECR pull-through cache
  • Loading branch information
danstn authored Apr 18, 2023
1 parent aca1d39 commit c0db21c
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.20@sha256:f7099345b8e4a93c62dc5102e7eb19a9cdbad12e7e322644eeaba355d70e616d
FROM 445615400570.dkr.ecr.us-east-1.amazonaws.com/ecr-public/docker/library/golang:1.20@sha256:f7099345b8e4a93c62dc5102e7eb19a9cdbad12e7e322644eeaba355d70e616d

RUN apt-get update \
&& apt-get install -y unzip \
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
default: build

build:
go build -o terraform-provider-buildkite .
go build -o terraform-provider-buildkite -ldflags="-s -w -X main.version=$(shell git describe --tag)" .

fmt:
go fmt ./...
Expand Down
36 changes: 24 additions & 12 deletions buildkite/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,29 @@ type Client struct {
http *http.Client
organization string
restUrl string
userAgent string
}

type clientConfig struct {
org string
apiToken string
graphqlURL string
restURL string
userAgent string
}

// NewClient creates a client to use for interacting with the Buildkite API
func NewClient(org, apiToken, graphqlUrl, restUrl string) *Client {
token := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiToken})
func NewClient(config *clientConfig) *Client {
token := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: config.apiToken})
httpClient := oauth2.NewClient(context.Background(), token)

return &Client{
graphql: graphql.NewClient(graphqlUrl, httpClient),
genqlient: genqlient.NewClient(graphqlUrl, httpClient),
graphql: graphql.NewClient(config.graphqlURL, httpClient),
genqlient: genqlient.NewClient(config.graphqlURL, httpClient),
http: httpClient,
organization: org,
restUrl: restUrl,
organization: config.org,
restUrl: config.restURL,
userAgent: config.userAgent,
}
}

Expand All @@ -42,7 +52,7 @@ func (client *Client) makeRequest(method string, path string, postData interface
if postData != nil {
jsonPayload, err := json.Marshal(postData)
if err != nil {
return err
return fmt.Errorf("failed to marshal request: %w", err)
}
bodyBytes = bytes.NewBuffer(jsonPayload)
}
Expand All @@ -51,25 +61,27 @@ func (client *Client) makeRequest(method string, path string, postData interface

req, err := http.NewRequest(method, url, bodyBytes)
if err != nil {
return err
return fmt.Errorf("failed to create request: %w", err)
}

req.Header.Add("User-Agent", client.userAgent)

resp, err := client.http.Do(req)
if err != nil {
return err
return fmt.Errorf("failed to send request: %w", err)
}
if resp.StatusCode >= 400 {
return fmt.Errorf("Buildkite API request failed: %s %s (status: %d)", method, url, resp.StatusCode)
}

defer resp.Body.Close()

responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
return fmt.Errorf("failed to read response: %w", err)
}

if err := json.Unmarshal(responseBody, responseObject); err != nil {
return err
return fmt.Errorf("failed to unmarshal response: %w", err)
}

return nil
Expand Down
55 changes: 38 additions & 17 deletions buildkite/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

const graphqlEndpoint = "https://graphql.buildkite.com/v1"
const restEndpoint = "https://api.buildkite.com"
const (
defaultGraphqlEndpoint = "https://graphql.buildkite.com/v1"
defaultRestEndpoint = "https://api.buildkite.com"
)

const (
SchemaKeyOrganization = "organization"
SchemaKeyAPIToken = "api_token"
SchemaKeyGraphqlURL = "graphql_url"
SchemaKeyRestURL = "rest_url"
)

// Provider creates the schema.Provider for Buildkite
func Provider() *schema.Provider {
return &schema.Provider{
func Provider(version string) *schema.Provider {
provider := &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"buildkite_agent_token": resourceAgentToken(),
"buildkite_pipeline": resourcePipeline(),
Expand All @@ -25,40 +34,52 @@ func Provider() *schema.Provider {
"buildkite_organization": dataSourceOrganization(),
},
Schema: map[string]*schema.Schema{
"organization": &schema.Schema{
SchemaKeyOrganization: {
DefaultFunc: schema.EnvDefaultFunc("BUILDKITE_ORGANIZATION", nil),
Description: "The Buildkite organization slug",
Required: true,
Type: schema.TypeString,
},
"api_token": &schema.Schema{
SchemaKeyAPIToken: {
DefaultFunc: schema.EnvDefaultFunc("BUILDKITE_API_TOKEN", nil),
Description: "API token with GraphQL access and `write_pipelines, read_pipelines` scopes",
Required: true,
Type: schema.TypeString,
},
"graphql_url": &schema.Schema{
DefaultFunc: schema.EnvDefaultFunc("BUILDKITE_GRAPHQL_URL", graphqlEndpoint),
SchemaKeyGraphqlURL: {
DefaultFunc: schema.EnvDefaultFunc("BUILDKITE_GRAPHQL_URL", defaultGraphqlEndpoint),
Description: "Base URL for the GraphQL API to use",
Optional: true,
Type: schema.TypeString,
},
"rest_url": &schema.Schema{
DefaultFunc: schema.EnvDefaultFunc("BUILDKITE_REST_URL", restEndpoint),
SchemaKeyRestURL: {
DefaultFunc: schema.EnvDefaultFunc("BUILDKITE_REST_URL", defaultRestEndpoint),
Description: "Base URL for the REST API to use",
Optional: true,
Type: schema.TypeString,
},
},
ConfigureFunc: providerConfigure,
}
provider.ConfigureFunc = providerConfigure(provider.UserAgent("buildkite", version))

return provider
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
orgName := d.Get("organization").(string)
apiToken := d.Get("api_token").(string)
graphqlUrl := d.Get("graphql_url").(string)
restUrl := d.Get("rest_url").(string)
func providerConfigure(userAgent string) func(d *schema.ResourceData) (interface{}, error) {
return func(d *schema.ResourceData) (interface{}, error) {
orgName := d.Get(SchemaKeyOrganization).(string)
apiToken := d.Get(SchemaKeyAPIToken).(string)
graphqlUrl := d.Get(SchemaKeyGraphqlURL).(string)
restUrl := d.Get(SchemaKeyRestURL).(string)

config := &clientConfig{
org: orgName,
apiToken: apiToken,
graphqlURL: graphqlUrl,
restURL: restUrl,
userAgent: userAgent,
}

return NewClient(orgName, apiToken, graphqlUrl, restUrl), nil
return NewClient(config), nil
}
}
6 changes: 3 additions & 3 deletions buildkite/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ var testAccProviders map[string]*schema.Provider
var testAccProvider *schema.Provider

func init() {
testAccProvider = Provider()
testAccProvider = Provider("")
testAccProviders = map[string]*schema.Provider{
"buildkite": testAccProvider,
}
}

// TestProvider just does basic validation to ensure the schema is defined and supporting functions exist
func TestProvider(t *testing.T) {
if err := Provider().InternalValidate(); err != nil {
if err := Provider("").InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}

func TestProvider_impl(t *testing.T) {
var _ *schema.Provider = Provider()
var _ *schema.Provider = Provider("")
}

func testAccPreCheck(t *testing.T) {
Expand Down
11 changes: 10 additions & 1 deletion buildkite/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ import (

func TestGetOrganizationIDMissing(t *testing.T) {
slug := "doesnt match API key"
client := NewClient(slug, os.Getenv("BUILDKITE_API_TOKEN"), graphqlEndpoint, restEndpoint)

config := &clientConfig{
org: slug,
apiToken: os.Getenv("BUILDKITE_API_TOKEN"),
graphqlURL: defaultGraphqlEndpoint,
restURL: defaultRestEndpoint,
userAgent: "test-user-agent",
}

client := NewClient(config)

id, err := GetOrganizationID(slug, client.graphql)
if err == nil {
Expand Down
10 changes: 9 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@ package main

import (
"github.com/buildkite/terraform-provider-buildkite/buildkite"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
)

// Set at compile time from ldflags
var (
version string
)

func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: buildkite.Provider,
ProviderFunc: func() *schema.Provider {
return buildkite.Provider(version)
},
})
}

0 comments on commit c0db21c

Please sign in to comment.