diff --git a/Dockerfile b/Dockerfile index 2bb3aef1..c84eb0f6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 \ diff --git a/Makefile b/Makefile index 13990751..21ea4ab1 100644 --- a/Makefile +++ b/Makefile @@ -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 ./... diff --git a/buildkite/client.go b/buildkite/client.go index 5147d1a5..7f53519d 100644 --- a/buildkite/client.go +++ b/buildkite/client.go @@ -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, } } @@ -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) } @@ -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 diff --git a/buildkite/provider.go b/buildkite/provider.go index a8541019..622e0dc5 100644 --- a/buildkite/provider.go +++ b/buildkite/provider.go @@ -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(), @@ -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 + } } diff --git a/buildkite/provider_test.go b/buildkite/provider_test.go index 06fe1ae7..da295605 100644 --- a/buildkite/provider_test.go +++ b/buildkite/provider_test.go @@ -16,7 +16,7 @@ var testAccProviders map[string]*schema.Provider var testAccProvider *schema.Provider func init() { - testAccProvider = Provider() + testAccProvider = Provider("") testAccProviders = map[string]*schema.Provider{ "buildkite": testAccProvider, } @@ -24,13 +24,13 @@ func init() { // 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) { diff --git a/buildkite/util_test.go b/buildkite/util_test.go index 69ca6ad7..87a97a78 100644 --- a/buildkite/util_test.go +++ b/buildkite/util_test.go @@ -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 { diff --git a/main.go b/main.go index 3b353c14..df54ccc1 100644 --- a/main.go +++ b/main.go @@ -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) + }, }) }