diff --git a/client/http/client.go b/client/http/client.go index 5df68bff..3eddf4a1 100644 --- a/client/http/client.go +++ b/client/http/client.go @@ -23,11 +23,19 @@ type HttpClient struct { client *resty.Client } -func NewHttpClient(apiKey string, apiSecret string, apiEndpoint string, restClient *resty.Client) (*HttpClient, error) { +type HttpClientConfig struct { + ApiKey string + ApiSecret string + ApiEndpoint string + UserAgent string + RestClient *resty.Client +} + +func NewHttpClient(config HttpClientConfig) (*HttpClient, error) { return &HttpClient{ - ApiKey: apiKey, - ApiSecret: apiSecret, - client: restClient.SetHostURL(apiEndpoint), + ApiKey: config.ApiKey, + ApiSecret: config.ApiSecret, + client: config.RestClient.SetHostURL(config.ApiEndpoint).SetHeader("User-Agent", config.UserAgent), }, nil } diff --git a/client/http/client_test.go b/client/http/client_test.go index 15158e56..acabc7ab 100644 --- a/client/http/client_test.go +++ b/client/http/client_test.go @@ -29,15 +29,24 @@ const BaseUrl = "https://fake.env0.com" const ApiKey = "MY_USER" const ApiSecret = "MY_PASS" const ExpectedBasicAuth = "Basic TVlfVVNFUjpNWV9QQVNT" - +const UserAgent = "super-cool-ua" const ErrorStatusCode = 500 const ErrorMessage = "Very bad!" + var httpclient *httpModule.HttpClient var _ = BeforeSuite(func() { // mock all HTTP requests restClient := resty.New() - httpclient, _ = httpModule.NewHttpClient(ApiKey, ApiSecret, BaseUrl, restClient) + + config := httpModule.HttpClientConfig{ + ApiKey: ApiKey, + ApiSecret: ApiSecret, + ApiEndpoint: BaseUrl, + UserAgent: UserAgent, + RestClient: restClient, + } + httpclient, _ = httpModule.NewHttpClient(config) httpmock.ActivateNonDefault(restClient.GetClient()) }) @@ -58,7 +67,7 @@ func TestHttpClient(t *testing.T) { var _ = Describe("Http Client", func() { var httpRequest *http.Request - mockRequest := RequestBody { + mockRequest := RequestBody{ Message: "I have a request", } mockedResponse := ResponseType{ @@ -69,7 +78,6 @@ var _ = Describe("Http Client", func() { failureURI := "/path/to/failure" successUrl := BaseUrl + successURI failureUrl := BaseUrl + failureURI - AssertAuth := func() { authorization := httpRequest.Header["Authorization"] @@ -82,19 +90,19 @@ var _ = Describe("Http Client", func() { } AssertError := func(err error) { - Expect(err.Error()).To(Equal(strconv.Itoa(ErrorStatusCode) + ": " + ErrorMessage), "Should return error message") + Expect(err.Error()).To(Equal(strconv.Itoa(ErrorStatusCode)+": "+ErrorMessage), "Should return error message") } AssertHttpCall := func(method string, url string) { methodAndUrl := method + " " + url // Validate call happened once callMap := httpmock.GetCallCountInfo() - Expect(callMap[methodAndUrl]).Should(Equal(1), "Should call " + methodAndUrl) + Expect(callMap[methodAndUrl]).Should(Equal(1), "Should call "+methodAndUrl) // Validate no other call happened delete(callMap, methodAndUrl) for unexpectedCall, amount := range callMap { - Expect(amount).To(BeZero(), "Should not call " + unexpectedCall) + Expect(amount).To(BeZero(), "Should not call "+unexpectedCall) } } @@ -123,7 +131,7 @@ var _ = Describe("Http Client", func() { }) httpmock.RegisterResponder(methodType, failureUrl, func(req *http.Request) (*http.Response, error) { httpRequest = req - return httpmock.NewStringResponse(ErrorStatusCode, ErrorMessage), nil + return httpmock.NewStringResponse(ErrorStatusCode, ErrorMessage), nil }) } }) diff --git a/env0/provider.go b/env0/provider.go index 551828c1..1fbbe510 100644 --- a/env0/provider.go +++ b/env0/provider.go @@ -2,67 +2,78 @@ package env0 import ( "context" - "github.com/env0/terraform-provider-env0/client" "github.com/env0/terraform-provider-env0/client/http" "github.com/go-resty/resty/v2" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" ) -func Provider() *schema.Provider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - "api_endpoint": { - Type: schema.TypeString, - Description: "override api endpoint (used for testing)", - DefaultFunc: schema.EnvDefaultFunc("ENV0_API_ENDPOINT", "https://api.env0.com/"), - Optional: true, +func Provider(version string) plugin.ProviderFunc { + return func() *schema.Provider { + provider := &schema.Provider{ + Schema: map[string]*schema.Schema{ + "api_endpoint": { + Type: schema.TypeString, + Description: "override api endpoint (used for testing)", + DefaultFunc: schema.EnvDefaultFunc("ENV0_API_ENDPOINT", "https://api.env0.com/"), + Optional: true, + }, + "api_key": { + Type: schema.TypeString, + Description: "env0 api key (https://docs.env0.com/reference#authentication)", + DefaultFunc: schema.EnvDefaultFunc("ENV0_API_KEY", nil), + Required: true, + Sensitive: true, + }, + "api_secret": { + Type: schema.TypeString, + Description: "env0 api key secret", + DefaultFunc: schema.EnvDefaultFunc("ENV0_API_SECRET", nil), + Required: true, + Sensitive: true, + }, }, - "api_key": { - Type: schema.TypeString, - Description: "env0 api key (https://docs.env0.com/reference#authentication)", - DefaultFunc: schema.EnvDefaultFunc("ENV0_API_KEY", nil), - Required: true, - Sensitive: true, + DataSourcesMap: map[string]*schema.Resource{ + "env0_organization": dataOrganization(), + "env0_project": dataProject(), + "env0_configuration_variable": dataConfigurationVariable(), + "env0_template": dataTemplate(), + "env0_ssh_key": dataSshKey(), + "env0_aws_credentials": dataAwsCredentials(), }, - "api_secret": { - Type: schema.TypeString, - Description: "env0 api key secret", - DefaultFunc: schema.EnvDefaultFunc("ENV0_API_SECRET", nil), - Required: true, - Sensitive: true, + ResourcesMap: map[string]*schema.Resource{ + "env0_project": resourceProject(), + "env0_configuration_variable": resourceConfigurationVariable(), + "env0_template": resourceTemplate(), + "env0_ssh_key": resourceSshKey(), + "env0_aws_credentials": resourceAwsCredentials(), + "env0_template_project_assignment": resourceTemplateProjectAssignment(), + "env0_cloud_credentials_project_assignment": resourceCloudCredentialsProjectAssignment(), }, - }, - DataSourcesMap: map[string]*schema.Resource{ - "env0_organization": dataOrganization(), - "env0_project": dataProject(), - "env0_configuration_variable": dataConfigurationVariable(), - "env0_template": dataTemplate(), - "env0_ssh_key": dataSshKey(), - "env0_aws_credentials": dataAwsCredentials(), - }, - ResourcesMap: map[string]*schema.Resource{ - "env0_project": resourceProject(), - "env0_configuration_variable": resourceConfigurationVariable(), - "env0_template": resourceTemplate(), - "env0_ssh_key": resourceSshKey(), - "env0_aws_credentials": resourceAwsCredentials(), - "env0_template_project_assignment": resourceTemplateProjectAssignment(), - "env0_cloud_credentials_project_assignment": resourceCloudCredentialsProjectAssignment(), - }, - ConfigureContextFunc: configureProvider, + } + + provider.ConfigureContextFunc = configureProvider(version, provider) + return provider } } -func configureProvider(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - apiKey := d.Get("api_key") - apiSecret := d.Get("api_secret") +func configureProvider(version string, p *schema.Provider) schema.ConfigureContextFunc { + userAgent := p.UserAgent("terraform-provider-env0", version) - httpClient, err := http.NewHttpClient(apiKey.(string), apiSecret.(string), d.Get("api_endpoint").(string), resty.New()) - if err != nil { - return nil, diag.Diagnostics{diag.Diagnostic{Severity: diag.Error, Summary: err.Error()}} - } + return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { + httpClient, err := http.NewHttpClient(http.HttpClientConfig{ + ApiKey: d.Get("api_key").(string), + ApiSecret: d.Get("api_secret").(string), + ApiEndpoint: d.Get("api_endpoint").(string), + UserAgent: userAgent, + RestClient: resty.New(), + }) + if err != nil { + return nil, diag.Diagnostics{diag.Diagnostic{Severity: diag.Error, Summary: err.Error()}} + } - return client.NewApiClient(httpClient), nil + return client.NewApiClient(httpClient), nil + } } diff --git a/env0/provider_test.go b/env0/provider_test.go index 44b07ddd..a2c7b543 100644 --- a/env0/provider_test.go +++ b/env0/provider_test.go @@ -22,7 +22,7 @@ var ( var testUnitProviders = map[string]func() (*schema.Provider, error){ "env0": func() (*schema.Provider, error) { - provider := Provider() + provider := Provider("")() provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { return apiClientMock, nil } @@ -49,7 +49,7 @@ func runUnitTest(t *testing.T, testCase resource.TestCase, mockFunc func(mockFun } func TestProvider(t *testing.T) { - if err := Provider().InternalValidate(); err != nil { + if err := Provider("")().InternalValidate(); err != nil { t.Fatalf("err: %s", err) } } @@ -74,12 +74,12 @@ func testMissingEnvVar(t *testing.T, envVars map[string]string, expectedKey stri defer os.Setenv(key, "") } - diags := Provider().Validate(&terraform.ResourceConfig{}) + diags := Provider("")().Validate(&terraform.ResourceConfig{}) testExpectedProviderError(t, diags, expectedKey) } func testMissingConfig(t *testing.T, config map[string]interface{}, expectedKey string) { - diags := Provider().Validate(terraform.NewResourceConfigRaw(config)) + diags := Provider("")().Validate(terraform.NewResourceConfigRaw(config)) testExpectedProviderError(t, diags, expectedKey) } diff --git a/main.go b/main.go index 0249eb07..256e01f3 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,35 @@ package main import ( + "context" + "flag" "github.com/env0/terraform-provider-env0/env0" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + "log" +) + +//go:generate terraform fmt -recursive ./examples/ + +var ( + // these will be set by the goreleaser configuration + // to appropriate values for the compiled binary + version string = "dev" ) func main() { - plugin.Serve(&plugin.ServeOpts{ - ProviderFunc: env0.Provider, - }) + var debugMode bool + flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve") + flag.Parse() + + opts := &plugin.ServeOpts{ProviderFunc: env0.Provider(version)} + + if debugMode { + err := plugin.Debug(context.Background(), "registry.terraform.io/env0/env0", opts) + if err != nil { + log.Fatal(err.Error()) + } + return + } + + plugin.Serve(opts) }