From a316da5b16459597fb30091a4c88f6315e09644d Mon Sep 17 00:00:00 2001 From: Marco Santonastaso Date: Mon, 9 Dec 2024 14:28:41 +0000 Subject: [PATCH] Provider client connection rework (#34) * feat: base client connection rework implemented and docs updated * feat: base client connection refactored and consolidated for both modes * feat: comments resolved --- README.md | 5 +- docs/index.md | 105 ++++++-- examples/README.md | 4 +- examples/provider/console_provider.tf | 18 ++ examples/provider/gateway_provider.tf | 17 ++ examples/provider/multi_provider.tf | 34 +++ examples/provider/provider.tf | 17 -- internal/client/client.go | 246 +++++++----------- internal/client/utils.go | 28 -- internal/model/provider_data.go | 4 +- .../gateway_service_account_v2_resource.go | 12 +- ...ateway_service_account_v2_resource_test.go | 10 +- internal/provider/generic_resource.go | 12 +- internal/provider/generic_resource_test.go | 6 +- internal/provider/group_v2_resource.go | 13 +- internal/provider/group_v2_resource_test.go | 10 +- .../provider/kafka_cluster_v2_resource.go | 13 +- .../kafka_cluster_v2_resource_test.go | 16 +- .../provider/kafka_connect_v2_resource.go | 13 +- .../kafka_connect_v2_resource_test.go | 14 +- internal/provider/provider.go | 222 +++++++--------- internal/provider/provider_test.go | 8 +- internal/provider/user_v2_resource.go | 13 +- internal/provider/user_v2_resource_test.go | 10 +- .../conduktor_provider_gen.go | 115 +++----- .../schema/validation/schema_validation.go | 4 + provider_code_spec.json | 92 +++---- templates/index.md.tmpl | 12 +- 28 files changed, 522 insertions(+), 551 deletions(-) create mode 100644 examples/provider/console_provider.tf create mode 100644 examples/provider/gateway_provider.tf create mode 100644 examples/provider/multi_provider.tf delete mode 100644 examples/provider/provider.tf diff --git a/README.md b/README.md index 42d3160..5671233 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,9 @@ terraform { ```hcl # configure provider provider "conduktor" { - console_url = "http://localhost:8080" - api_token = "your-api-key" # can also use admin email/password to authenticate. + mode = "console" + base_url = "http://localhost:8080" + api_token = "your-api-key" # can also use admin email/password to authenticate. } # register an external user bob with PLATFORM.userView permission diff --git a/docs/index.md b/docs/index.md index 4014501..6275d4f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,45 +16,106 @@ The Conduktor provider is used to interact with the resources supported by Condu ## Example Usage +### Console client only + ```terraform provider "conduktor" { + mode = "console" # mandatory console URL - console_url = "http://localhost:8080" # or env vars CDK_CONSOLE_URL or CDK_BASE_URL + base_url = "http://localhost:8080" # or env vars CDK_CONSOLE_BASE_URL or CDK_BASE_URL # authentication either with api token or admin credentials api_token = "your-api-token" # or env var CDK_API_TOKEN or CDK_API_KEY - #admin_email = "admin@my-org.com" # or env var CDK_ADMIN_EMAIL - #admin_password = "admin-password" # or env var CDK_ADMIN_PASSWORD + #admin_user = "admin@my-org.com" # or env var CDK_CONSOLE_USER or CDK_ADMIN_EMAIL or CDK_ADMIN_USER + #admin_password = "admin-password" # or env var CDK_CONSOLE_PASSWORD or CDK_ADMIN_PASSWORD + + # optional http client TLS configuration + cert = file("path/to/cert.pem") # or env var CDK_CONSOLE_CERT or CDK_CERT + insecure = true # or env var CDK_CONSOLE_INSECURE or CDK_INSECURE + + # optional authentication via certificate + key = file("path/to/key.pem") # or env var CDK_CONSOLE_KEY or CDK_KEY + cacert = file("path/to/ca.pem") # or env var CDK_CONSOLE_CA_CERT CDK_CA_CERT +} +``` + +### Gateway client only + +```terraform +provider "conduktor" { + mode = "gateway" + # mandatory gateway URL + base_url = "http://localhost:8888" # or env vars CDK_GATEWAY_BASE_URL or CDK_BASE_URL + + # authentication with admin credentials + admin_user = "admin" # or env var CDK_GATEWAY_USER or CDK_ADMIN_USER + admin_password = "admin-password" # or env var CDK_GATEWAY_PASSWORD or CDK_ADMIN_PASSWORD # optional http client TLS configuration - cert = file("path/to/cert.pem") # or env var CDK_CERT - insecure = true # or env var CDK_INSECURE + cert = file("path/to/cert.pem") # or env var CDK_GATEWAY_CERT or CDK_CERT + insecure = true # or env var CDK_GATEWAY_INSECURE or CDK_INSECURE # optional authentication via certificate - key = file("path/to/key.pem") # or env var CDK_KEY - cacert = file("path/to/ca.pem") # or env var CDK_CA_CERT + key = file("path/to/key.pem") # or env var CDK_GATEWAY_KEY or CDK_KEY + cacert = file("path/to/ca.pem") # or env var CDK_GATEWAY_CACERT or CDK_CACERT +} +``` + +### Multi client configuration using [terraform alias](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations) + +```terraform +provider "conduktor" { + alias = "console" + mode = "console" + base_url = "http://localhost:8080" + + api_token = "your-api-token" + #admin_user = "admin@my-org.com" + #admin_password = "admin-password" + + insecure = true +} + +provider "conduktor" { + alias = "gateway" + mode = "gateway" + base_url = "http://localhost:8888" + + admin_user = "admin" + admin_password = "admin-password" + + insecure = true +} + +# And how to use them with example resources +# NOTE: Console resources will be prefixed with console_ in the future to avoid confusion +resource "conduktor_user_v2" "user" { + provider = conduktor.console + # ... +} + +resource "conduktor_gateway_service_account_v2" "gateway_sa" { + provider = conduktor.gateway + # ... } ``` ## Schema +### Required + +- `mode` (String) The mode that you would like to set for the terraform provider. May be set using environment variable `CDK_PROVIDER_MODE`. Can either be one of `console` or `gateway` + ### Optional -- `admin_email` (String) The email of the admin user. May be set using environment variable `CDK_ADMIN_EMAIL`. Required if admin_password is set. If not provided, the API token will be used to authenticate. -- `admin_password` (String, Sensitive) The password of the admin user. May be set using environment variable `CDK_ADMIN_PASSWORD`. Required if admin_email is set. If not provided, the API token will be used to authenticater. -- `api_token` (String, Sensitive) The API token to authenticate with the Conduktor API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_email and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information. -- `cacert` (String) Root CA certificate in PEM format to verify the Conduktor Console certificate. May be set using environment variable `CDK_CACERT`. If not provided, the system's root CA certificates will be used. -- `cert` (String) Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CERT`. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication. -- `console_url` (String) The URL of the Conduktor Console. May be set using environment variable `CDK_BASE_URL` or `CDK_CONSOLE_URL`. Required either here or in the environment. -- `gateway_cacert` (String) Root CA certificate in PEM format to verify the Conduktor Gateway certificate. May be set using environment variable `CDK_GATEWAY_CACERT`. If not provided, the system's root CA certificates will be used. -- `gateway_cert` (String) Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_CERT`. Must be used with key. If key is provided, cert is required. Useful when Gateway is behind a reverse proxy with client certificate authentication. -- `gateway_insecure` (Boolean) Skip TLS verification flag. May be set using environment variable `CDK_GATEWAY_INSECURE`. -- `gateway_key` (String) Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Gateway is behind a reverse proxy with client certificate authentication. -- `gateway_password` (String, Sensitive) The password of Gateway the admin user. May be set using environment variable `CDK_GATEWAY_PASSWORD`. Required if gateway_url is set. -- `gateway_url` (String) The administration URL of the Conduktor Gateway. May also be set using environment variable `CDK_GATEWAY_BASE_URL`. Required either here or in the environment. -- `gateway_user` (String) The login of a Gateway admin user. May be set using environment variable `CDK_GATEWAY_USER`. Required if gateway_url is set. -- `insecure` (Boolean) Skip TLS verification flag. May be set using environment variable `CDK_INSECURE`. -- `key` (String) Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication. +- `admin_password` (String, Sensitive) The password of the admin user. May be set using environment variable `CDK_CONSOLE_PASSWORD` or `CDK_ADMIN_PASSWORD` for Console, `CDK_GATEWAY_PASSWORD` or `CDK_ADMIN_PASSWORD` for Gateway. Required if admin_email is set. If not provided, the API token will be used to authenticater. +- `admin_user` (String) The login credentials of the admin user. May be set using environment variable `CDK_CONSOLE_USER`, `CDK_ADMIN_EMAIL` or `CDK_ADMIN_USER` for Console, `CDK_GATEWAY_USER` or `CDK_ADMIN_USER` for Gateway. Required if admin_password is set. If not provided and `mode` is Console, the API token will be used to authenticate. +- `api_token` (String, Sensitive) The API token to authenticate with the Conduktor Console API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_user and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information. Not used if `mode` is Gateway. +- `base_url` (String) The URL of either Conduktor Console or Gateway, depending on the `mode`. May be set using environment variable `CDK_CONSOLE_BASE_URL` or `CDK_BASE_URL` for Console, `CDK_GATEWAY_BASE_URL` or `CDK_BASE_URL` for Gateway. Required either here or in the environment. +- `cacert` (String) Root CA certificate in PEM format to verify the Conduktor certificate. May be set using environment variable `CDK_CONSOLE_CACERT` or `CDK_CACERT` for Console, `CDK_GATEWAY_CACERT` or `CDK_CACERT` for Gateway. If not provided, the system's root CA certificates will be used. +- `cert` (String) Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_CERT` or `CDK_CERT` for Console, `CDK_GATEWAY_CERT` or `CDK_CERT` for Gateway. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication. +- `insecure` (Boolean) Skip TLS verification flag. May be set using environment variable `CDK_CONSOLE_INSECURE` or `CDK_INSECURE` for Console, `CDK_GATEWAY_INSECURE` or `CDK_INSECURE` for Gateway. +- `key` (String) Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_KEY` or `CDK_KEY` for Console, `CDK_GATEWAY_KEY` or `CDK_KEY` for Gateway. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication. diff --git a/examples/README.md b/examples/README.md index 026c42c..3c0f611 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,6 +4,8 @@ This directory contains examples that are mostly used for documentation, but can The document generation tool looks for files in the following locations by default. All other *.tf files besides the ones mentioned below are ignored by the documentation tool. This is useful for creating examples that can run and/or ar testable even if some parts are not relevant for the documentation. -* **provider/provider.tf** example file for the provider index page +* **provider/console_provider.tf** example file for the provider index page in console mode +* **provider/gateway_provider.tf** example file for the provider index page in gateway mode +* **provider/multi_provider.tf** example file for the provider index page in multi client configuration * **data-sources/`full data source name`/data-source.tf** example file for the named data source page * **resources/`full resource name`/resource.tf** example file for the named data source page diff --git a/examples/provider/console_provider.tf b/examples/provider/console_provider.tf new file mode 100644 index 0000000..8dd7be0 --- /dev/null +++ b/examples/provider/console_provider.tf @@ -0,0 +1,18 @@ +provider "conduktor" { + mode = "console" + # mandatory console URL + base_url = "http://localhost:8080" # or env vars CDK_CONSOLE_BASE_URL or CDK_BASE_URL + + # authentication either with api token or admin credentials + api_token = "your-api-token" # or env var CDK_API_TOKEN or CDK_API_KEY + #admin_user = "admin@my-org.com" # or env var CDK_CONSOLE_USER or CDK_ADMIN_EMAIL or CDK_ADMIN_USER + #admin_password = "admin-password" # or env var CDK_CONSOLE_PASSWORD or CDK_ADMIN_PASSWORD + + # optional http client TLS configuration + cert = file("path/to/cert.pem") # or env var CDK_CONSOLE_CERT or CDK_CERT + insecure = true # or env var CDK_CONSOLE_INSECURE or CDK_INSECURE + + # optional authentication via certificate + key = file("path/to/key.pem") # or env var CDK_CONSOLE_KEY or CDK_KEY + cacert = file("path/to/ca.pem") # or env var CDK_CONSOLE_CA_CERT CDK_CA_CERT +} diff --git a/examples/provider/gateway_provider.tf b/examples/provider/gateway_provider.tf new file mode 100644 index 0000000..316fc78 --- /dev/null +++ b/examples/provider/gateway_provider.tf @@ -0,0 +1,17 @@ +provider "conduktor" { + mode = "gateway" + # mandatory gateway URL + base_url = "http://localhost:8888" # or env vars CDK_GATEWAY_BASE_URL or CDK_BASE_URL + + # authentication with admin credentials + admin_user = "admin" # or env var CDK_GATEWAY_USER or CDK_ADMIN_USER + admin_password = "admin-password" # or env var CDK_GATEWAY_PASSWORD or CDK_ADMIN_PASSWORD + + # optional http client TLS configuration + cert = file("path/to/cert.pem") # or env var CDK_GATEWAY_CERT or CDK_CERT + insecure = true # or env var CDK_GATEWAY_INSECURE or CDK_INSECURE + + # optional authentication via certificate + key = file("path/to/key.pem") # or env var CDK_GATEWAY_KEY or CDK_KEY + cacert = file("path/to/ca.pem") # or env var CDK_GATEWAY_CACERT or CDK_CACERT +} diff --git a/examples/provider/multi_provider.tf b/examples/provider/multi_provider.tf new file mode 100644 index 0000000..a3f4688 --- /dev/null +++ b/examples/provider/multi_provider.tf @@ -0,0 +1,34 @@ +provider "conduktor" { + alias = "console" + mode = "console" + base_url = "http://localhost:8080" + + api_token = "your-api-token" + #admin_user = "admin@my-org.com" + #admin_password = "admin-password" + + insecure = true +} + +provider "conduktor" { + alias = "gateway" + mode = "gateway" + base_url = "http://localhost:8888" + + admin_user = "admin" + admin_password = "admin-password" + + insecure = true +} + +# And how to use them with example resources +# NOTE: Console resources will be prefixed with console_ in the future to avoid confusion +resource "conduktor_user_v2" "user" { + provider = conduktor.console + # ... +} + +resource "conduktor_gateway_service_account_v2" "gateway_sa" { + provider = conduktor.gateway + # ... +} diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf deleted file mode 100644 index 481ce8f..0000000 --- a/examples/provider/provider.tf +++ /dev/null @@ -1,17 +0,0 @@ -provider "conduktor" { - # mandatory console URL - console_url = "http://localhost:8080" # or env vars CDK_CONSOLE_URL or CDK_BASE_URL - - # authentication either with api token or admin credentials - api_token = "your-api-token" # or env var CDK_API_TOKEN or CDK_API_KEY - #admin_email = "admin@my-org.com" # or env var CDK_ADMIN_EMAIL - #admin_password = "admin-password" # or env var CDK_ADMIN_PASSWORD - - # optional http client TLS configuration - cert = file("path/to/cert.pem") # or env var CDK_CERT - insecure = true # or env var CDK_INSECURE - - # optional authentication via certificate - key = file("path/to/key.pem") # or env var CDK_KEY - cacert = file("path/to/ca.pem") # or env var CDK_CA_CERT -} diff --git a/internal/client/client.go b/internal/client/client.go index d7f2811..167cf94 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -14,19 +14,20 @@ import ( jsoniter "github.com/json-iterator/go" ) -type ConsoleClient struct { - apiKey string +// Enum used to perform different actions based on provider mode. +// Setting as string so it can be used for log messages. +type Mode string + +const ( + CONSOLE Mode = "Console" + GATEWAY Mode = "Gateway" +) + +type Client struct { baseUrl string client *resty.Client } -type GatewayClient struct { - GatewayUser string - GatewayPassword string - baseUrl string - client *resty.Client -} - type ApiParameter struct { ApiKey string BaseUrl string @@ -35,13 +36,6 @@ type ApiParameter struct { TLSParameters TLSParameters } -type GatewayApiParameters struct { - BaseUrl string - GatewayUser string - GatewayPassword string - TLSParameters TLSParameters -} - type TLSParameters struct { Key string Cert string @@ -61,85 +55,92 @@ type ApplyResult struct { Resource interface{} `json:"resource"` } -func Make(ctx context.Context, apiParameter ApiParameter, providerVersion string) (*ConsoleClient, error) { +func Make(ctx context.Context, mode Mode, apiParameter ApiParameter, providerVersion string) (*Client, error) { restyClient := resty.New().SetHeader("X-CDK-CLIENT", "TF/"+providerVersion) + if mode == CONSOLE { + apiParameter.BaseUrl = uniformizeBaseUrl(apiParameter.BaseUrl) + } + var err error // Enable http client debug logs when provider log is set to TRACE restyClient.SetDebug(TraceLogEnabled()) - if (apiParameter.CdkUser != "" && apiParameter.CdkPassword == "") || (apiParameter.CdkUser == "" && apiParameter.CdkPassword != "") { - return nil, fmt.Errorf("CDK_USER and CDK_PASSWORD must be provided together") - } - if apiParameter.CdkUser != "" && apiParameter.ApiKey != "" { - return nil, fmt.Errorf("Can't set both CDK_USER and CDK_API_KEY") + restyClient, err = ConfigureTLS(ctx, restyClient, apiParameter.TLSParameters) + if err != nil { + return nil, err } - restyClient, err := ConfigureTLS(ctx, restyClient, apiParameter.TLSParameters) + restyClient, err = ConfigureAuth(ctx, mode, restyClient, apiParameter) if err != nil { return nil, err } - result := &ConsoleClient{ - apiKey: apiParameter.ApiKey, - baseUrl: uniformizeBaseUrl(apiParameter.BaseUrl), + return &Client{ + baseUrl: apiParameter.BaseUrl, client: restyClient, - } - - if apiParameter.CdkUser != "" { - retry3Time := retry(3, 1*time.Second, ctx) - loginResult, err := retry3Time( - func(err error) bool { - return err.Error() == "Invalid username or password" - }, func() (interface{}, error) { - return result.login(apiParameter.CdkUser, apiParameter.CdkPassword) - }, - ) + }, nil +} - if err != nil { - return nil, fmt.Errorf("Could not login: %s", err) +func ConfigureAuth(ctx context.Context, mode Mode, restyClient *resty.Client, apiParameter ApiParameter) (*resty.Client, error) { + var err error + switch mode { + case CONSOLE: + { + apiKey := apiParameter.ApiKey + if apiKey == "" { + // Only Login with username and password if no apiKey has been provided. + apiKey, err = Login(apiParameter, restyClient) + if err != nil { + return nil, fmt.Errorf("Could not login: %s", err) + } + } + + restyClient = restyClient.SetAuthScheme("Bearer") + restyClient = restyClient.SetAuthToken(apiKey) + } + case GATEWAY: + { + restyClient.SetBasicAuth(apiParameter.CdkUser, apiParameter.CdkPassword) + + // Testing authentication parameters against /metrics API. + // Returning error after 3 retries. + testUrl := apiParameter.BaseUrl + "/metrics" + resp, err := restyClient.SetRetryCount(3).SetRetryWaitTime(1 * time.Second).R().Get(testUrl) + if err != nil { + return nil, err + } else if resp.StatusCode() != 200 { + return nil, fmt.Errorf("Invalid username or password") + } } - tokens, _ := loginResult.(LoginResult) - - result.apiKey = tokens.AccessToken - } - - if result.apiKey != "" { - result.client = result.client.SetAuthScheme("Bearer") - result.client = result.client.SetAuthToken(result.apiKey) } - return result, nil + return restyClient, nil } -func MakeGateway(ctx context.Context, apiParameter GatewayApiParameters, providerVersion string) (*GatewayClient, error) { - restyClient := resty.New().SetHeader("X-CDK-CLIENT", "TF/"+providerVersion) - - // Enable http client debug logs when provider log is set to TRACE - restyClient.SetDebug(TraceLogEnabled()) +// Helper function for Console Auth flow to retrieve access token. +func Login(apiParameter ApiParameter, client *resty.Client) (string, error) { + url := apiParameter.BaseUrl + "/login" + body := map[string]string{ + "username": apiParameter.CdkUser, + "password": apiParameter.CdkPassword, + } - restyClient, err := ConfigureTLS(ctx, restyClient, apiParameter.TLSParameters) + resp, err := client.SetRetryCount(3).SetRetryWaitTime(1 * time.Second).R().SetBody(body).Post(url) if err != nil { - return nil, err + return "", err + } else if resp.IsError() { + if resp.StatusCode() == 401 { + return "", fmt.Errorf("Invalid username or password") + } else { + return "", fmt.Errorf("%s", extractApiError(resp)) + } } - - restyClient.SetBasicAuth(apiParameter.GatewayUser, apiParameter.GatewayPassword) - - // Testing authentication parameters against /metrics API - // returning error after 3 retries - testUrl := apiParameter.BaseUrl + "/metrics" - resp, err := restyClient.SetRetryCount(3).SetRetryWaitTime(1 * time.Second).R().Get(testUrl) + result := LoginResult{} + err = jsoniter.Unmarshal(resp.Body(), &result) if err != nil { - return &GatewayClient{}, err - } else if resp.StatusCode() != 200 { - return &GatewayClient{}, fmt.Errorf("Invalid username or password") + return "", err } - - return &GatewayClient{ - GatewayUser: apiParameter.GatewayUser, - GatewayPassword: apiParameter.GatewayPassword, - baseUrl: apiParameter.BaseUrl, - client: restyClient, - }, nil + return result.AccessToken, nil } func ConfigureTLS(ctx context.Context, restyClient *resty.Client, tlsParameter TLSParameters) (*resty.Client, error) { @@ -167,27 +168,7 @@ func ConfigureTLS(ctx context.Context, restyClient *resty.Client, tlsParameter T return restyClient, nil } -func (client *ConsoleClient) login(username, password string) (LoginResult, error) { - url := client.baseUrl + "/login" - resp, err := client.client.R().SetBody(map[string]string{"username": username, "password": password}).Post(url) - if err != nil { - return LoginResult{}, err - } else if resp.IsError() { - if resp.StatusCode() == 401 { - return LoginResult{}, fmt.Errorf("Invalid username or password") - } else { - return LoginResult{}, fmt.Errorf("%s", extractApiError(resp)) - } - } - result := LoginResult{} - err = jsoniter.Unmarshal(resp.Body(), &result) - if err != nil { - return LoginResult{}, err - } - return result, nil -} - -func (client *ConsoleClient) ApplyGeneric(ctx context.Context, cliResource ctlresource.Resource) (string, error) { +func (client *Client) ApplyGeneric(ctx context.Context, cliResource ctlresource.Resource) (string, error) { kinds := ctlschema.ConsoleDefaultKind() // TODO support gateway kind and client too kindName := cliResource.Kind @@ -220,7 +201,7 @@ func (client *ConsoleClient) ApplyGeneric(ctx context.Context, cliResource ctlre return upsertResponse.UpsertResult, nil } -func (client *ConsoleClient) Apply(ctx context.Context, path string, resource interface{}) (ApplyResult, error) { +func (client *Client) Apply(ctx context.Context, path string, resource interface{}) (ApplyResult, error) { url := client.baseUrl + path jsonData, err := jsoniter.Marshal(resource) if err != nil { @@ -245,7 +226,7 @@ func (client *ConsoleClient) Apply(ctx context.Context, path string, resource in return upsertResponse, nil } -func (client *ConsoleClient) Describe(ctx context.Context, path string) ([]byte, error) { +func (client *Client) Describe(ctx context.Context, path string) ([]byte, error) { url := client.baseUrl + path resp, err := client.client.R().Get(url) if err != nil { @@ -260,73 +241,28 @@ func (client *ConsoleClient) Describe(ctx context.Context, path string) ([]byte, return resp.Body(), nil } -func (client *ConsoleClient) Delete(ctx context.Context, path string) error { +func (client *Client) Delete(ctx context.Context, mode Mode, path string, resource interface{}) error { + var req *resty.Request url := client.baseUrl + path tflog.Trace(ctx, fmt.Sprintf("DELETE %s", path)) - resp, err := client.client.R().Delete(url) - if err != nil { - return err - } else if resp.IsError() { - return fmt.Errorf("%s", extractApiError(resp)) - } - - return nil -} - -// This is a temporary workaround - will be revisited with the future client works. -func (client *GatewayClient) Apply(ctx context.Context, path string, resource interface{}) (ApplyResult, error) { - url := client.baseUrl + path - jsonData, err := jsoniter.Marshal(resource) - if err != nil { - return ApplyResult{}, fmt.Errorf("Error marshalling resource: %s", err) - } - - tflog.Trace(ctx, fmt.Sprintf("PUT %s request body : %s", path, string(jsonData))) - builder := client.client.R().SetBody(jsonData) - resp, err := builder.Put(url) - if err != nil { - return ApplyResult{}, err - } else if resp.IsError() { - return ApplyResult{}, fmt.Errorf("%s", extractApiError(resp)) - } - bodyBytes := resp.Body() - tflog.Trace(ctx, fmt.Sprintf("PUT %s response body : %s", path, string(bodyBytes))) - var upsertResponse ApplyResult - err = jsoniter.Unmarshal(bodyBytes, &upsertResponse) - if err != nil { - return ApplyResult{}, fmt.Errorf("Error unmarshalling response: %s", err) - } - return upsertResponse, nil -} -func (client *GatewayClient) Describe(ctx context.Context, path string) ([]byte, error) { - url := client.baseUrl + path - resp, err := client.client.R().Get(url) - if err != nil { - return []byte{}, err - } else if resp.IsError() { - if resp.StatusCode() == 404 { - return nil, nil + if mode == CONSOLE { + req = client.client.R() + } else if mode == GATEWAY { + // Gateway API handles deletion in a different way + // It needs information about the resource in the body of the request + // as opposed to Console API who needs them in the URL + jsonData, err := json.Marshal(resource) + if err != nil { + return fmt.Errorf("Error marshalling resource: %s", err) } - return []byte{}, fmt.Errorf("error describing resources %s, got status code: %d:\n %s", path, resp.StatusCode(), string(resp.Body())) - } - tflog.Trace(ctx, fmt.Sprintf("GET %s response : %s", path, string(resp.Body()))) - return resp.Body(), nil -} - -func (client *GatewayClient) Delete(ctx context.Context, path string, resource interface{}) error { - url := client.baseUrl + path + tflog.Debug(ctx, string(jsonData)) + tflog.Trace(ctx, fmt.Sprintf("DELETE %s request body : %s", path, string(jsonData))) - jsonData, err := json.Marshal(resource) - if err != nil { - return fmt.Errorf("Error marshalling resource: %s", err) + req = client.client.R().SetBody(string(jsonData)) } - tflog.Debug(ctx, string(jsonData)) - tflog.Trace(ctx, fmt.Sprintf("PUT %s request body : %s", path, string(jsonData))) - builder := client.client.R().SetBody(string(jsonData)) - tflog.Trace(ctx, fmt.Sprintf("DELETE %s", path)) - resp, err := builder.Delete(url) + resp, err := req.Delete(url) if err != nil { return err } else if resp.IsError() { diff --git a/internal/client/utils.go b/internal/client/utils.go index 7d7725b..0d77b6a 100644 --- a/internal/client/utils.go +++ b/internal/client/utils.go @@ -1,16 +1,12 @@ package client import ( - "context" "encoding/json" - "fmt" "os" "regexp" "strings" - "time" "github.com/go-resty/resty/v2" - "github.com/hashicorp/terraform-plugin-log/tflog" ) type ApiError struct { @@ -41,30 +37,6 @@ func uniformizeBaseUrl(baseUrl string) string { return regex.ReplaceAllString(baseUrl, "/api") } -func retry(attempts int, delay time.Duration, ctx context.Context) func(filter func(error) bool, f func() (interface{}, error)) (interface{}, error) { - return func(filter func(error) bool, f func() (interface{}, error)) (interface{}, error) { - var result interface{} - var err error - for i := 0; i < attempts; i++ { - result, err = f() - if err != nil { - if filter != nil && !filter(err) { - return nil, err - } - if i < attempts-1 { - tflog.Warn(ctx, fmt.Sprintf("Retrying after error: %v", err)) - time.Sleep(delay) - continue - } else { - return nil, err - } - } - break - } - return result, nil - } -} - // hack to guess if current provider logs are in trace level. func TraceLogEnabled() bool { selfLevel := strings.ToUpper(os.Getenv("TF_LOG_PROVIDER_CONDUKTOR")) diff --git a/internal/model/provider_data.go b/internal/model/provider_data.go index e4cfdbf..6408a2f 100644 --- a/internal/model/provider_data.go +++ b/internal/model/provider_data.go @@ -3,6 +3,6 @@ package model import "github.com/conduktor/terraform-provider-conduktor/internal/client" type ProviderData struct { - ConsoleClient *client.ConsoleClient - GatewayClient *client.GatewayClient + Mode *client.Mode + Client *client.Client } diff --git a/internal/provider/gateway_service_account_v2_resource.go b/internal/provider/gateway_service_account_v2_resource.go index daaac16..aefa202 100644 --- a/internal/provider/gateway_service_account_v2_resource.go +++ b/internal/provider/gateway_service_account_v2_resource.go @@ -26,7 +26,7 @@ func NewGatewayServiceAccountV2Resource() resource.Resource { // GatewayServiceAccountV2Resource defines the resource implementation. type GatewayServiceAccountV2Resource struct { - apiClient *client.GatewayClient + apiClient *client.Client } func (r *GatewayServiceAccountV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -54,15 +54,17 @@ func (r *GatewayServiceAccountV2Resource) Configure(ctx context.Context, req res return } - if data.GatewayClient == nil { + if data.Client == nil || data.Mode != client.GATEWAY { resp.Diagnostics.AddError( "Unexpected Resource Configure Type", - "Gateway Client not configured. Please provide client configuration details for Gateway API.", + "Gateway Client not configured. Please provide client configuration details for Gateway API and ensure you have set the right provider mode for this resource. \n"+ + "More info here: \n"+ + " - https://registry.terraform.io/providers/conduktor/conduktor/latest/docs", ) return } - r.apiClient = data.GatewayClient + r.apiClient = data.Client } func (r *GatewayServiceAccountV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -219,7 +221,7 @@ func (r *GatewayServiceAccountV2Resource) Delete(ctx context.Context, req resour VCluster: data.Vcluster.ValueString(), } - err := r.apiClient.Delete(ctx, gatewayServiceAccountV2ApiPath, deleteRes) + err := r.apiClient.Delete(ctx, client.GATEWAY, gatewayServiceAccountV2ApiPath, deleteRes) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete service account, got error: %s", err)) return diff --git a/internal/provider/gateway_service_account_v2_resource_test.go b/internal/provider/gateway_service_account_v2_resource_test.go index d7c4a67..e51b037 100644 --- a/internal/provider/gateway_service_account_v2_resource_test.go +++ b/internal/provider/gateway_service_account_v2_resource_test.go @@ -17,7 +17,7 @@ func TestAccGatewayServiceAccountV2Resource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "gateway_service_account_v2_resource_create.tf"), + Config: providerConfigGateway + test.TestAccTestdata(t, "gateway_service_account_v2_resource_create.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "user1"), resource.TestCheckResourceAttr(resourceRef, "vcluster", "vcluster_sa"), @@ -36,7 +36,7 @@ func TestAccGatewayServiceAccountV2Resource(t *testing.T) { }, // Update and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "gateway_service_account_v2_resource_update.tf"), + Config: providerConfigGateway + test.TestAccTestdata(t, "gateway_service_account_v2_resource_update.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "user1"), resource.TestCheckResourceAttr(resourceRef, "vcluster", "vcluster_sa"), @@ -58,7 +58,7 @@ func TestAccGatewayServiceAccountV2Minimal(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from minimal example { - Config: providerConfig + test.TestAccTestdata(t, "gateway_service_account_v2_resource_minimal.tf"), + Config: providerConfigGateway + test.TestAccTestdata(t, "gateway_service_account_v2_resource_minimal.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_gateway_service_account_v2.minimal", "name", "minimal"), resource.TestCheckResourceAttr("conduktor_gateway_service_account_v2.minimal", "vcluster", "passthrough"), @@ -79,7 +79,7 @@ func TestAccGatewayServiceAccountV2ExampleResource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from simple example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_gateway_service_account_v2", "simple.tf"), + Config: providerConfigGateway + test.TestAccExample(t, "resources", "conduktor_gateway_service_account_v2", "simple.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_gateway_service_account_v2.local_sa", "name", "simple-service-account"), resource.TestCheckResourceAttr("conduktor_gateway_service_account_v2.local_sa", "vcluster", "passthrough"), @@ -88,7 +88,7 @@ func TestAccGatewayServiceAccountV2ExampleResource(t *testing.T) { }, // Create and Read from complex example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_gateway_service_account_v2", "complex.tf"), + Config: providerConfigGateway + test.TestAccExample(t, "resources", "conduktor_gateway_service_account_v2", "complex.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_gateway_service_account_v2.external_sa", "name", "complex-service-account"), resource.TestCheckResourceAttr("conduktor_gateway_service_account_v2.external_sa", "vcluster", "vcluster_sa"), diff --git a/internal/provider/generic_resource.go b/internal/provider/generic_resource.go index d1a8ff8..1156bac 100644 --- a/internal/provider/generic_resource.go +++ b/internal/provider/generic_resource.go @@ -25,7 +25,7 @@ func NewGenericResource() resource.Resource { // GenericResource defines the resource implementation. type GenericResource struct { - client *client.ConsoleClient + client *client.Client } func (r *GenericResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -53,15 +53,17 @@ func (r *GenericResource) Configure(ctx context.Context, req resource.ConfigureR return } - if data.ConsoleClient == nil { + if data.Client == nil || data.Mode != client.CONSOLE { resp.Diagnostics.AddError( "Unexpected Resource Configure Type", - "Console Client not configured. Please provide client configuration details for Console API.", + "Console Client not configured. Please provide client configuration details for Console API and ensure you have set the right provider mode for this resource. \n"+ + "More info here: \n"+ + " - https://registry.terraform.io/providers/conduktor/conduktor/latest/docs", ) return } - r.client = data.ConsoleClient + r.client = data.Client } func (r *GenericResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -211,7 +213,7 @@ func (r *GenericResource) Delete(ctx context.Context, req resource.DeleteRequest } tflog.Debug(ctx, fmt.Sprintf("Delete resource on path %s", resourcePath)) - err = r.client.Delete(ctx, resourcePath) + err = r.client.Delete(ctx, client.CONSOLE, resourcePath, nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete Generic, got error: %s", err)) return diff --git a/internal/provider/generic_resource_test.go b/internal/provider/generic_resource_test.go index 2d2a318..87a7097 100644 --- a/internal/provider/generic_resource_test.go +++ b/internal/provider/generic_resource_test.go @@ -15,7 +15,7 @@ func TestAccGenericResource(t *testing.T) { Steps: []resource.TestStep{ // Create embedded and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "generic_resource_create_embedded.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "generic_resource_create_embedded.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "jim.halpert@dunder.mifflin.com"), resource.TestCheckResourceAttr(resourceRef, "kind", "User"), @@ -28,7 +28,7 @@ func TestAccGenericResource(t *testing.T) { // ImportState testing // Update and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "generic_resource_update_embedded.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "generic_resource_update_embedded.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "jim.halpert@dunder.mifflin.com"), resource.TestCheckResourceAttr(resourceRef, "kind", "User"), @@ -50,7 +50,7 @@ func TestAccGenericExample2Resource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_generic", "embedded.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_generic", "embedded.tf"), ExpectNonEmptyPlan: true, // Example YAML are not properly formatted to prevent dirty plan on apply Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_generic.example", "name", "bob@company.io"), diff --git a/internal/provider/group_v2_resource.go b/internal/provider/group_v2_resource.go index e8a7ebe..d8ca057 100644 --- a/internal/provider/group_v2_resource.go +++ b/internal/provider/group_v2_resource.go @@ -26,7 +26,7 @@ func NewGroupV2Resource() resource.Resource { // GroupV2Resource defines the resource implementation. type GroupV2Resource struct { - apiClient *client.ConsoleClient + apiClient *client.Client } func (r *GroupV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -54,15 +54,17 @@ func (r *GroupV2Resource) Configure(ctx context.Context, req resource.ConfigureR return } - if data.ConsoleClient == nil { + if data.Client == nil || data.Mode != client.CONSOLE { resp.Diagnostics.AddError( "Unexpected Resource Configure Type", - "Console Client not configured. Please provide client configuration details for Console API.", + "Console Client not configured. Please provide client configuration details for Console API and ensure you have set the right provider mode for this resource. \n"+ + "More info here: \n"+ + " - https://registry.terraform.io/providers/conduktor/conduktor/latest/docs", ) return } - r.apiClient = data.ConsoleClient + r.apiClient = data.Client } func (r *GroupV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -208,7 +210,8 @@ func (r *GroupV2Resource) Delete(ctx context.Context, req resource.DeleteRequest return } - err := r.apiClient.Delete(ctx, fmt.Sprintf("%s/%s", groupV2ApiPath, data.Name.ValueString())) + resourcePath := fmt.Sprintf("%s/%s", groupV2ApiPath, data.Name.ValueString()) + err := r.apiClient.Delete(ctx, client.CONSOLE, resourcePath, nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete group, got error: %s", err)) return diff --git a/internal/provider/group_v2_resource_test.go b/internal/provider/group_v2_resource_test.go index 6ad2165..40f93c3 100644 --- a/internal/provider/group_v2_resource_test.go +++ b/internal/provider/group_v2_resource_test.go @@ -17,7 +17,7 @@ func TestAccGroupV2Resource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "group_v2_resource_create.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "group_v2_resource_create.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "sales"), resource.TestCheckResourceAttr(resourceRef, "spec.display_name", "Sales Department"), @@ -41,7 +41,7 @@ func TestAccGroupV2Resource(t *testing.T) { }, // Update and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "group_v2_resource_update.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "group_v2_resource_update.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "sales"), resource.TestCheckResourceAttr(resourceRef, "spec.display_name", "New Sales Department"), @@ -75,7 +75,7 @@ func TestAccGroupV2Minimal(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from minimal example { - Config: providerConfig + test.TestAccTestdata(t, "group_v2_resource_minimal.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "group_v2_resource_minimal.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_group_v2.minimal", "name", "minimal"), resource.TestCheckResourceAttr("conduktor_group_v2.minimal", "spec.display_name", "Minimal"), @@ -99,7 +99,7 @@ func TestAccGroupV2ExampleResource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from simple example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_group_v2", "simple.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_group_v2", "simple.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_group_v2.example", "name", "simple-group"), resource.TestCheckResourceAttr("conduktor_group_v2.example", "spec.display_name", "Simple Group"), @@ -112,7 +112,7 @@ func TestAccGroupV2ExampleResource(t *testing.T) { }, // Create and Read from complex example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_group_v2", "complex.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_group_v2", "complex.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_group_v2.example", "name", "complex-group"), resource.TestCheckResourceAttr("conduktor_group_v2.example", "spec.display_name", "Complex group"), diff --git a/internal/provider/kafka_cluster_v2_resource.go b/internal/provider/kafka_cluster_v2_resource.go index bfad53c..a040250 100644 --- a/internal/provider/kafka_cluster_v2_resource.go +++ b/internal/provider/kafka_cluster_v2_resource.go @@ -25,7 +25,7 @@ func NewKafkaClusterV2Resource() resource.Resource { // KafkaClusterV2Resource defines the resource implementation. type KafkaClusterV2Resource struct { - apiClient *client.ConsoleClient + apiClient *client.Client } func (r *KafkaClusterV2Resource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -53,15 +53,17 @@ func (r *KafkaClusterV2Resource) Configure(_ context.Context, req resource.Confi return } - if data.ConsoleClient == nil { + if data.Client == nil || data.Mode != client.CONSOLE { resp.Diagnostics.AddError( "Unexpected Resource Configure Type", - "Console Client not configured. Please provide client configuration details for Console API.", + "Console Client not configured. Please provide client configuration details for Console API and ensure you have set the right provider mode for this resource. \n"+ + "More info here: \n"+ + " - https://registry.terraform.io/providers/conduktor/conduktor/latest/docs", ) return } - r.apiClient = data.ConsoleClient + r.apiClient = data.Client } func (r *KafkaClusterV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -207,7 +209,8 @@ func (r *KafkaClusterV2Resource) Delete(ctx context.Context, req resource.Delete return } - err := r.apiClient.Delete(ctx, fmt.Sprintf("%s/%s", kafkaClusterV2ApiPath, data.Name.ValueString())) + resourcePath := fmt.Sprintf("%s/%s", kafkaClusterV2ApiPath, data.Name.ValueString()) + err := r.apiClient.Delete(ctx, client.CONSOLE, resourcePath, nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete kafka cluster, got error: %s", err)) return diff --git a/internal/provider/kafka_cluster_v2_resource_test.go b/internal/provider/kafka_cluster_v2_resource_test.go index 52327d5..62fb394 100644 --- a/internal/provider/kafka_cluster_v2_resource_test.go +++ b/internal/provider/kafka_cluster_v2_resource_test.go @@ -16,7 +16,7 @@ func TestAccKafkaClusterV2Resource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "kafka_cluster_v2_resource_create.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "kafka_cluster_v2_resource_create.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "test-cluster"), resource.TestCheckResourceAttr(resourceRef, "labels.%", "1"), @@ -51,7 +51,7 @@ func TestAccKafkaClusterV2Resource(t *testing.T) { }, // Update and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "kafka_cluster_v2_resource_update.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "kafka_cluster_v2_resource_update.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "test-cluster"), resource.TestCheckResourceAttr(resourceRef, "labels.%", "2"), @@ -91,7 +91,7 @@ func TestAccKafkaClusterV2Minimal(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from minimal example { - Config: providerConfig + test.TestAccTestdata(t, "kafka_cluster_v2_resource_minimal.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "kafka_cluster_v2_resource_minimal.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "mini-cluster"), resource.TestCheckResourceAttr(resourceRef, "spec.display_name", "Minimal Cluster"), @@ -122,7 +122,7 @@ func TestAccKafkaClusterV2ExampleResource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from simple example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "simple.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "simple.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(simpleResourceRef, "name", "simple-cluster"), resource.TestCheckResourceAttr(simpleResourceRef, "labels.%", "0"), @@ -133,7 +133,7 @@ func TestAccKafkaClusterV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "gateway.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "gateway.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(gatewayResourceRef, "name", "gateway-cluster"), resource.TestCheckResourceAttr(gatewayResourceRef, "labels.%", "1"), @@ -154,7 +154,7 @@ func TestAccKafkaClusterV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "aiven.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "aiven.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(aivenResourceRef, "name", "aiven-cluster"), resource.TestCheckResourceAttr(aivenResourceRef, "labels.%", "1"), @@ -175,7 +175,7 @@ func TestAccKafkaClusterV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "aws_msk.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "aws_msk.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(awsResourceRef, "name", "aws-cluster"), resource.TestCheckResourceAttr(awsResourceRef, "labels.%", "1"), @@ -192,7 +192,7 @@ func TestAccKafkaClusterV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "confluent.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_cluster_v2", "confluent.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(confluentResourceRef, "name", "confluent-cluster"), resource.TestCheckResourceAttr(confluentResourceRef, "labels.%", "1"), diff --git a/internal/provider/kafka_connect_v2_resource.go b/internal/provider/kafka_connect_v2_resource.go index 9d0f74d..de368fd 100644 --- a/internal/provider/kafka_connect_v2_resource.go +++ b/internal/provider/kafka_connect_v2_resource.go @@ -32,7 +32,7 @@ func NewKafkaConnectV2Resource() resource.Resource { // KafkaConnectV2Resource defines the resource implementation. type KafkaConnectV2Resource struct { - apiClient *client.ConsoleClient + apiClient *client.Client } func (r *KafkaConnectV2Resource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -60,15 +60,17 @@ func (r *KafkaConnectV2Resource) Configure(_ context.Context, req resource.Confi return } - if data.ConsoleClient == nil { + if data.Client == nil || data.Mode != client.CONSOLE { resp.Diagnostics.AddError( "Unexpected Resource Configure Type", - "Console Client not configured. Please provide client configuration details for Console API.", + "Console Client not configured. Please provide client configuration details for Console API and ensure you have set the right provider mode for this resource. \n"+ + "More info here: \n"+ + " - https://registry.terraform.io/providers/conduktor/conduktor/latest/docs", ) return } - r.apiClient = data.ConsoleClient + r.apiClient = data.Client } func (r *KafkaConnectV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -214,7 +216,8 @@ func (r *KafkaConnectV2Resource) Delete(ctx context.Context, req resource.Delete return } - err := r.apiClient.Delete(ctx, kafkaConnectV2ApiGetPath(data.Cluster.ValueString(), data.Name.ValueString())) + resourcePath := kafkaConnectV2ApiGetPath(data.Cluster.ValueString(), data.Name.ValueString()) + err := r.apiClient.Delete(ctx, client.CONSOLE, resourcePath, nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete kafka connect server, got error: %s", err)) return diff --git a/internal/provider/kafka_connect_v2_resource_test.go b/internal/provider/kafka_connect_v2_resource_test.go index fc2ecb4..d435dc4 100644 --- a/internal/provider/kafka_connect_v2_resource_test.go +++ b/internal/provider/kafka_connect_v2_resource_test.go @@ -16,7 +16,7 @@ func TestAccKafkaConnectV2Resource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "kafka_connect_v2_resource_create.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "kafka_connect_v2_resource_create.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "test-connect"), resource.TestCheckResourceAttr(resourceRef, "cluster", "mini-cluster"), @@ -42,7 +42,7 @@ func TestAccKafkaConnectV2Resource(t *testing.T) { }, // Update and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "kafka_connect_v2_resource_update.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "kafka_connect_v2_resource_update.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "test-connect"), resource.TestCheckResourceAttr(resourceRef, "cluster", "mini-cluster"), @@ -75,7 +75,7 @@ func TestAccKafkaConnectV2Minimal(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from minimal example { - Config: providerConfig + test.TestAccTestdata(t, "kafka_connect_v2_resource_minimal.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "kafka_connect_v2_resource_minimal.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "minimal-connect"), resource.TestCheckResourceAttr(resourceRef, "cluster", "mini-cluster"), @@ -107,7 +107,7 @@ func TestAccKafkaConnectV2ExampleResource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from simple example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "simple.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "simple.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(simpleResourceRef, "name", "simple-connect"), resource.TestCheckResourceAttr(simpleResourceRef, "cluster", "mini-cluster"), @@ -119,7 +119,7 @@ func TestAccKafkaConnectV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "mtls.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "mtls.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(mtlsResourceRef, "name", "mtls-connect"), resource.TestCheckResourceAttr(mtlsResourceRef, "cluster", "mini-cluster"), @@ -137,7 +137,7 @@ func TestAccKafkaConnectV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "basicAuth.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "basicAuth.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(basicResourceRef, "name", "basic-connect"), resource.TestCheckResourceAttr(basicResourceRef, "cluster", "mini-cluster"), @@ -157,7 +157,7 @@ func TestAccKafkaConnectV2ExampleResource(t *testing.T) { ), }, { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "bearerToken.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_kafka_connect_v2", "bearerToken.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(bearerResourceRef, "name", "bearer-connect"), resource.TestCheckResourceAttr(bearerResourceRef, "cluster", "mini-cluster"), diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 36d1df8..2e9a23a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -2,6 +2,7 @@ package provider import ( "context" + "strings" "github.com/conduktor/terraform-provider-conduktor/internal/client" schemaUtils "github.com/conduktor/terraform-provider-conduktor/internal/schema" @@ -32,8 +33,8 @@ type ConduktorProvider struct { } type ProviderData struct { - ConsoleClient *client.ConsoleClient - GatewayClient *client.GatewayClient + Mode client.Mode + Client *client.Client } func (p *ConduktorProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { @@ -46,153 +47,122 @@ func (p *ConduktorProvider) Schema(ctx context.Context, req provider.SchemaReque } func (p *ConduktorProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { - var config schema.ConduktorModel - var consoleApiClient *client.ConsoleClient - var gatewayApiClient *client.GatewayClient + var input schema.ConduktorModel + var apiClient *client.Client + var data ProviderData var err error - resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + resp.Diagnostics.Append(req.Config.Get(ctx, &input)...) if resp.Diagnostics.HasError() { return } - consoleUrl := schemaUtils.GetStringConfig(config.ConsoleUrl, []string{"CDK_BASE_URL", "CDK_CONSOLE_URL"}) - apiToken := schemaUtils.GetStringConfig(config.ApiToken, []string{"CDK_API_TOKEN", "CDK_API_KEY"}) - adminEmail := schemaUtils.GetStringConfig(config.AdminEmail, []string{"CDK_ADMIN_EMAIL"}) - adminPassword := schemaUtils.GetStringConfig(config.AdminPassword, []string{"CDK_ADMIN_PASSWORD"}) - gatewayUrl := schemaUtils.GetStringConfig(config.GatewayUrl, []string{"CDK_GATEWAY_BASE_URL"}) - gatewayUser := schemaUtils.GetStringConfig(config.GatewayUser, []string{"CDK_GATEWAY_USER"}) - gatewayPassword := schemaUtils.GetStringConfig(config.GatewayPassword, []string{"CDK_GATEWAY_PASSWORD"}) - cert := schemaUtils.GetStringConfig(config.Cert, []string{"CDK_CERT"}) - cacert := schemaUtils.GetStringConfig(config.Cacert, []string{"CDK_CACERT"}) - key := schemaUtils.GetStringConfig(config.Key, []string{"CDK_KEY"}) - insecure := schemaUtils.GetBooleanConfig(config.Insecure, []string{"CDK_INSECURE"}, false) - gatewayCert := schemaUtils.GetStringConfig(config.GatewayCert, []string{"CDK_GATEWAY_CERT"}) - gatewayCacert := schemaUtils.GetStringConfig(config.GatewayCacert, []string{"CDK_GATEWAY_CACERT"}) - gatewayKey := schemaUtils.GetStringConfig(config.GatewayKey, []string{"CDK_GATEWAY_KEY"}) - gatewayInsecure := schemaUtils.GetBooleanConfig(config.GatewayInsecure, []string{"CDK_GATEWAY_INSECURE"}, false) - - // Validate mandatory configurations - if consoleUrl == "" && gatewayUrl == "" { - details := "The provider cannot create any API client as there is a missing or empty value for both the Console and Gateway URL. " + - "Set either : " + - " - the console_url value in the configuration or use the CDK_BASE_URL or CDK_CONSOLE_URL environment variable. " + - " - the gateway_url value in the configuration or use the CDK_GATEWAY_BASE_URL environment variable. " + - "If either is already set, ensure the value is not empty." - - resp.Diagnostics.AddAttributeError(path.Root("console_url"), "Missing Console URL", details) - resp.Diagnostics.AddAttributeError(path.Root("gateway_url"), "Missing Gateway URL", details) - } - - if consoleUrl != "" { - if apiToken == "" && adminEmail == "" && adminPassword == "" { - details := "The provider cannot create the Console API client as there is a missing or empty value for the API Token and missing or empty values for the admin email/password. " + - "Set either : " + - " - the api_token value in the configuration or use the CDK_API_TOKEN environment variable. " + - " - the admin_email and admin_password value in the configuration or use the CDK_ADMIN_EMAIL/CDK_ADMIN_PASSWORD environment variable. " + - "If either is already set, ensure the value is not empty." - - resp.Diagnostics.AddAttributeError(path.Root("api_token"), "Missing API token", details) - resp.Diagnostics.AddAttributeError(path.Root("admin_email"), "Missing Admin email", details) - resp.Diagnostics.AddAttributeError(path.Root("admin_password"), "Missing Admin password", details) - } - } + mode := strings.ToLower(schemaUtils.GetStringConfig(input.Mode, []string{"CDK_PROVIDER_MODE"})) - if gatewayUrl != "" && (gatewayUser == "" || gatewayPassword == "") { - details := "The provider cannot create the Gateway API client as there is a missing or empty value for the admin email/password. " + - "Set both : " + - " - the gateway_user value in the configuration or use the CDK_GATEWAY_USER environment variable. " + - " - the gateway_password value in the configuration or use the CDK_GATEWAY_PASSWORD environment variable. " + - "If either is already set, ensure the value is not empty." - - resp.Diagnostics.AddAttributeError(path.Root("gateway_user"), "Missing Gateway Admin login", details) - resp.Diagnostics.AddAttributeError(path.Root("gateway_password"), "Missing Gateway Admin password", details) - } + // Data will only contain the mode being either CONSOLE or GATEWAY + apiParameter, data, resp := p.PreFlightChecks(mode, input, resp) if resp.Diagnostics.HasError() { return } - ctx = tflog.SetField(ctx, "console_url", consoleUrl) - ctx = tflog.SetField(ctx, "api_token", apiToken) - ctx = tflog.SetField(ctx, "admin_email", adminEmail) - ctx = tflog.SetField(ctx, "admin_password", adminPassword) - ctx = tflog.SetField(ctx, "gateway_url", gatewayUrl) - ctx = tflog.SetField(ctx, "gateway_user", gatewayUser) - ctx = tflog.SetField(ctx, "gateway_password", gatewayPassword) - ctx = tflog.SetField(ctx, "cert", cert) - ctx = tflog.SetField(ctx, "cacert", cacert) - ctx = tflog.SetField(ctx, "key", key) - ctx = tflog.SetField(ctx, "insecure", insecure) - ctx = tflog.SetField(ctx, "gateway_cert", gatewayCert) - ctx = tflog.SetField(ctx, "gateway_cacert", gatewayCacert) - ctx = tflog.SetField(ctx, "gateway_key", gatewayKey) - ctx = tflog.SetField(ctx, "gateway_insecure", gatewayInsecure) + ctx = tflog.SetField(ctx, "mode", mode) + ctx = tflog.SetField(ctx, "api_token", apiParameter.ApiKey) + ctx = tflog.SetField(ctx, "base_url", apiParameter.BaseUrl) + ctx = tflog.SetField(ctx, "admin_user", apiParameter.CdkUser) + ctx = tflog.SetField(ctx, "admin_password", apiParameter.CdkPassword) + ctx = tflog.SetField(ctx, "cert", apiParameter.TLSParameters.Cert) + ctx = tflog.SetField(ctx, "cacert", apiParameter.TLSParameters.Cacert) + ctx = tflog.SetField(ctx, "key", apiParameter.TLSParameters.Key) + ctx = tflog.SetField(ctx, "insecure", apiParameter.TLSParameters.Insecure) // Avoid leaking sensitive information in logs ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "api_token") ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "admin_password") - ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "gateway_password") ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "key") - ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "gatewayKey") - tflog.Debug(ctx, "Creating Conduktor Console client") - - // Create Console or Gateway clients only if the respective URL is provided - if consoleUrl != "" { - consoleApiClient, err = client.Make(ctx, - client.ApiParameter{ - ApiKey: apiToken, - BaseUrl: consoleUrl, - CdkUser: adminEmail, - CdkPassword: adminPassword, - TLSParameters: client.TLSParameters{ - Key: key, - Cert: cert, - Cacert: cacert, - Insecure: insecure, - }, - }, - p.version, - ) - if err != nil { - resp.Diagnostics.AddError("Could not create the Conduktor Console API client", err.Error()) - return - } + + tflog.Debug(ctx, "Creating Conduktor client for: "+string(data.Mode)) + + apiClient, err = client.Make(ctx, data.Mode, apiParameter, p.version) + + if err != nil { + resp.Diagnostics.AddError("Could not create the Conduktor "+string(data.Mode)+" API client", err.Error()) + return } - if gatewayUrl != "" { - gatewayApiClient, err = client.MakeGateway(ctx, - client.GatewayApiParameters{ - BaseUrl: gatewayUrl, - GatewayUser: gatewayUser, - GatewayPassword: gatewayPassword, - TLSParameters: client.TLSParameters{ - Key: gatewayKey, - Cert: gatewayCert, - Cacert: gatewayCacert, - Insecure: gatewayInsecure, - }, - }, - p.version, - ) - if err != nil { - resp.Diagnostics.AddError("Could not create the Conduktor Gateway API client", err.Error()) - return + data.Client = apiClient + + tflog.Info(ctx, "Configured Conduktor "+string(data.Mode)+" client", map[string]any{"success": true}) + + resp.DataSourceData = &data + resp.ResourceData = &data +} + +func (p *ConduktorProvider) PreFlightChecks(mode string, input schema.ConduktorModel, resp *provider.ConfigureResponse) (client.ApiParameter, ProviderData, *provider.ConfigureResponse) { + var data ProviderData + var apiParameter client.ApiParameter + + switch mode { + case "console": + { + data.Mode = client.CONSOLE + apiParameter.BaseUrl = schemaUtils.GetStringConfig(input.BaseUrl, []string{"CDK_CONSOLE_BASE_URL", "CDK_BASE_URL"}) + apiParameter.ApiKey = schemaUtils.GetStringConfig(input.ApiToken, []string{"CDK_API_TOKEN", "CDK_API_KEY"}) + apiParameter.CdkUser = schemaUtils.GetStringConfig(input.AdminUser, []string{"CDK_CONSOLE_USER", "CDK_ADMIN_EMAIL", "CDK_ADMIN_USER"}) + apiParameter.CdkPassword = schemaUtils.GetStringConfig(input.AdminPassword, []string{"CDK_CONSOLE_PASSWORD", "CDK_ADMIN_PASSWORD"}) + apiParameter.TLSParameters.Key = schemaUtils.GetStringConfig(input.Cert, []string{"CDK_CONSOLE_CERT", "CDK_CERT"}) + apiParameter.TLSParameters.Cacert = schemaUtils.GetStringConfig(input.Cacert, []string{"CDK_CONSOLE_CACERT", "CDK_CACERT"}) + apiParameter.TLSParameters.Key = schemaUtils.GetStringConfig(input.Key, []string{"CDK_CONSOLE_KEY", "CDK_KEY"}) + apiParameter.TLSParameters.Insecure = schemaUtils.GetBooleanConfig(input.Insecure, []string{"CDK_CONSOLE_INSECURE", "CDK_INSECURE"}, false) + + if apiParameter.ApiKey == "" { + // We only need to check user and password if no apiToken is provided. + if apiParameter.CdkUser == "" || apiParameter.CdkPassword == "" { + details := "The provider cannot create the Console API client as there is a missing or empty value for the API Token and missing or empty values for the admin user and password. \n" + + "Set either : \n" + + " - the api_token value in the configuration or use the CDK_API_TOKEN environment variable. \n" + + " - the admin_user and admin_password value in the configuration or use the CDK_ADMIN_EMAIL and CDK_ADMIN_PASSWORD environment variable. \n" + + "If either is already set, ensure the value is not empty." + + resp.Diagnostics.AddAttributeError(path.Root("api_token"), "Missing API token", details) + resp.Diagnostics.AddAttributeError(path.Root("admin_user"), "Missing Admin email", details) + resp.Diagnostics.AddAttributeError(path.Root("admin_password"), "Missing Admin password", details) + } + } + } + case "gateway": + { + data.Mode = client.GATEWAY + apiParameter.BaseUrl = schemaUtils.GetStringConfig(input.BaseUrl, []string{"CDK_GATEWAY_BASE_URL", "CDK_BASE_URL"}) + apiParameter.CdkUser = schemaUtils.GetStringConfig(input.AdminUser, []string{"CDK_GATEWAY_USER", "CDK_ADMIN_USER"}) + apiParameter.CdkPassword = schemaUtils.GetStringConfig(input.AdminPassword, []string{"CDK_GATEWAY_PASSWORD", "CDK_ADMIN_PASSWORD"}) + apiParameter.TLSParameters.Cacert = schemaUtils.GetStringConfig(input.Cert, []string{"CDK_GATEWAY_CERT", "CDK_CERT"}) + apiParameter.TLSParameters.Cacert = schemaUtils.GetStringConfig(input.Cacert, []string{"CDK_GATEWAY_CACERT", "CDK_CACERT"}) + apiParameter.TLSParameters.Key = schemaUtils.GetStringConfig(input.Key, []string{"CDK_GATEWAY_KEY", "CDK_KEY"}) + apiParameter.TLSParameters.Insecure = schemaUtils.GetBooleanConfig(input.Insecure, []string{"CDK_GATEWAY_INSECURE", "CDK_INSECURE"}, false) + + if apiParameter.CdkUser == "" || apiParameter.CdkPassword == "" { + details := "The provider cannot create the Gateway API client as there is a missing or empty value for the admin user and password. \n" + + "Set both : \n" + + " - the admin_user value in the configuration or use the CDK_GATEWAY_USER environment variable. \n" + + " - the admin_password value in the configuration or use the CDK_GATEWAY_PASSWORD environment variable. \n" + + "If either is already set, ensure the value is not empty." + + resp.Diagnostics.AddAttributeError(path.Root("admin_user"), "Missing Gateway Admin login", details) + resp.Diagnostics.AddAttributeError(path.Root("admin_password"), "Missing Gateway Admin password", details) + } } } - data := &ProviderData{ - ConsoleClient: consoleApiClient, - GatewayClient: gatewayApiClient, - } - resp.DataSourceData = data - resp.ResourceData = data + if apiParameter.BaseUrl == "" { + details := "The provider cannot create any API client as there is a missing or empty value for the Base URL. \n" + + "Set: \n" + + " - the base_url value in the configuration or use the following environment variables: CDK_BASE_URL or CDK_CONSOLE_BASE_URL for Console, CDK_GATEWAY_BASE_URL for Gateway. \n" + + "If either is already set, ensure the value is not empty." - if consoleApiClient != nil { - tflog.Info(ctx, "Configured Conduktor Console client", map[string]any{"success": true}) - } - if gatewayApiClient != nil { - tflog.Info(ctx, "Configured Conduktor Gateway client", map[string]any{"success": true}) + resp.Diagnostics.AddAttributeError(path.Root("base_url"), "Missing Console URL", details) } + + return apiParameter, data, resp } func (p *ConduktorProvider) Resources(ctx context.Context) []func() resource.Resource { diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 5dc208f..b52eb49 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -10,8 +10,14 @@ const ( // test configuration so the Conduktor Console client is properly configured. // It is also possible to use the CDK_ environment variables instead, // such as updating the Makefile and running the testing through that tool. - providerConfig = ` + providerConfigConsole = ` provider "conduktor" { + mode = "console" +} +` + providerConfigGateway = ` +provider "conduktor" { + mode = "gateway" } ` ) diff --git a/internal/provider/user_v2_resource.go b/internal/provider/user_v2_resource.go index 14eb350..3cf22ed 100644 --- a/internal/provider/user_v2_resource.go +++ b/internal/provider/user_v2_resource.go @@ -26,7 +26,7 @@ func NewUserV2Resource() resource.Resource { // UserV2Resource defines the resource implementation. type UserV2Resource struct { - apiClient *client.ConsoleClient + apiClient *client.Client } func (r *UserV2Resource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { @@ -54,15 +54,17 @@ func (r *UserV2Resource) Configure(ctx context.Context, req resource.ConfigureRe return } - if data.ConsoleClient == nil { + if data.Client == nil || data.Mode != client.CONSOLE { resp.Diagnostics.AddError( "Unexpected Resource Configure Type", - "Console Client not configured. Please provide client configuration details for Console API.", + "Console Client not configured. Please provide client configuration details for Console API and ensure you have set the right provider mode for this resource. \n"+ + "More info here: \n"+ + " - https://registry.terraform.io/providers/conduktor/conduktor/latest/docs", ) return } - r.apiClient = data.ConsoleClient + r.apiClient = data.Client } func (r *UserV2Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { @@ -207,7 +209,8 @@ func (r *UserV2Resource) Delete(ctx context.Context, req resource.DeleteRequest, if resp.Diagnostics.HasError() { return } - err := r.apiClient.Delete(ctx, fmt.Sprintf("%s/%s", userV2ApiPath, data.Name.ValueString())) + resourcePath := fmt.Sprintf("%s/%s", userV2ApiPath, data.Name.ValueString()) + err := r.apiClient.Delete(ctx, client.CONSOLE, resourcePath, nil) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete user, got error: %s", err)) return diff --git a/internal/provider/user_v2_resource_test.go b/internal/provider/user_v2_resource_test.go index ac62a4a..6b47aca 100644 --- a/internal/provider/user_v2_resource_test.go +++ b/internal/provider/user_v2_resource_test.go @@ -17,7 +17,7 @@ func TestAccUserV2Resource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "user_v2_resource_create.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "user_v2_resource_create.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "pam.beesly@dunder.mifflin.com"), resource.TestCheckResourceAttr(resourceRef, "spec.firstname", "Pam"), @@ -40,7 +40,7 @@ func TestAccUserV2Resource(t *testing.T) { }, // Update and Read testing { - Config: providerConfig + test.TestAccTestdata(t, "user_v2_resource_update.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "user_v2_resource_update.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceRef, "name", "pam.beesly@dunder.mifflin.com"), resource.TestCheckResourceAttr(resourceRef, "spec.firstname", "Pam"), @@ -67,7 +67,7 @@ func TestAccUserV2Minimal(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from minimal example { - Config: providerConfig + test.TestAccTestdata(t, "user_v2_resource_minimal.tf"), + Config: providerConfigConsole + test.TestAccTestdata(t, "user_v2_resource_minimal.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_user_v2.minimal", "name", "angela.martin@dunder-mifflin.com"), resource.TestCheckResourceAttr("conduktor_user_v2.minimal", "spec.permissions.#", "0"), @@ -86,7 +86,7 @@ func TestAccUserV2ExampleResource(t *testing.T) { Steps: []resource.TestStep{ // Create and Read from example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_user_v2", "simple.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_user_v2", "simple.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_user_v2.example", "name", "bob@company.io"), resource.TestCheckResourceAttr("conduktor_user_v2.example", "spec.firstname", "Bob"), @@ -96,7 +96,7 @@ func TestAccUserV2ExampleResource(t *testing.T) { }, // Create and Read from example { - Config: providerConfig + test.TestAccExample(t, "resources", "conduktor_user_v2", "complex.tf"), + Config: providerConfigConsole + test.TestAccExample(t, "resources", "conduktor_user_v2", "complex.tf"), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("conduktor_user_v2.example", "name", "bob@company.io"), resource.TestCheckResourceAttr("conduktor_user_v2.example", "spec.firstname", "Bob"), diff --git a/internal/schema/provider_conduktor/conduktor_provider_gen.go b/internal/schema/provider_conduktor/conduktor_provider_gen.go index 30fea0b..069482e 100644 --- a/internal/schema/provider_conduktor/conduktor_provider_gen.go +++ b/internal/schema/provider_conduktor/conduktor_provider_gen.go @@ -4,6 +4,9 @@ package provider_conduktor import ( "context" + "github.com/conduktor/terraform-provider-conduktor/internal/schema/validation" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/provider/schema" @@ -12,102 +15,68 @@ import ( func ConduktorProviderSchema(ctx context.Context) schema.Schema { return schema.Schema{ Attributes: map[string]schema.Attribute{ - "admin_email": schema.StringAttribute{ - Optional: true, - Description: "The email of the admin user. May be set using environment variable `CDK_ADMIN_EMAIL`. Required if admin_password is set. If not provided, the API token will be used to authenticate.", - MarkdownDescription: "The email of the admin user. May be set using environment variable `CDK_ADMIN_EMAIL`. Required if admin_password is set. If not provided, the API token will be used to authenticate.", - }, "admin_password": schema.StringAttribute{ Optional: true, Sensitive: true, - Description: "The password of the admin user. May be set using environment variable `CDK_ADMIN_PASSWORD`. Required if admin_email is set. If not provided, the API token will be used to authenticater.", - MarkdownDescription: "The password of the admin user. May be set using environment variable `CDK_ADMIN_PASSWORD`. Required if admin_email is set. If not provided, the API token will be used to authenticater.", - }, - "api_token": schema.StringAttribute{ - Optional: true, - Sensitive: true, - Description: "The API token to authenticate with the Conduktor API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_email and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information.", - MarkdownDescription: "The API token to authenticate with the Conduktor API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_email and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information.", - }, - "cacert": schema.StringAttribute{ - Optional: true, - Description: "Root CA certificate in PEM format to verify the Conduktor Console certificate. May be set using environment variable `CDK_CACERT`. If not provided, the system's root CA certificates will be used.", - MarkdownDescription: "Root CA certificate in PEM format to verify the Conduktor Console certificate. May be set using environment variable `CDK_CACERT`. If not provided, the system's root CA certificates will be used.", - }, - "cert": schema.StringAttribute{ - Optional: true, - Description: "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CERT`. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication.", - MarkdownDescription: "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CERT`. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication.", + Description: "The password of the admin user. May be set using environment variable `CDK_CONSOLE_PASSWORD` or `CDK_ADMIN_PASSWORD` for Console, `CDK_GATEWAY_PASSWORD` or `CDK_ADMIN_PASSWORD` for Gateway. Required if admin_email is set. If not provided, the API token will be used to authenticater.", + MarkdownDescription: "The password of the admin user. May be set using environment variable `CDK_CONSOLE_PASSWORD` or `CDK_ADMIN_PASSWORD` for Console, `CDK_GATEWAY_PASSWORD` or `CDK_ADMIN_PASSWORD` for Gateway. Required if admin_email is set. If not provided, the API token will be used to authenticater.", }, - "console_url": schema.StringAttribute{ + "admin_user": schema.StringAttribute{ Optional: true, - Description: "The URL of the Conduktor Console. May be set using environment variable `CDK_BASE_URL` or `CDK_CONSOLE_URL`. Required either here or in the environment.", - MarkdownDescription: "The URL of the Conduktor Console. May be set using environment variable `CDK_BASE_URL` or `CDK_CONSOLE_URL`. Required either here or in the environment.", + Description: "The login credentials of the admin user. May be set using environment variable `CDK_CONSOLE_USER`, `CDK_ADMIN_EMAIL` or `CDK_ADMIN_USER` for Console, `CDK_GATEWAY_USER` or `CDK_ADMIN_USER` for Gateway. Required if admin_password is set. If not provided and `mode` is Console, the API token will be used to authenticate.", + MarkdownDescription: "The login credentials of the admin user. May be set using environment variable `CDK_CONSOLE_USER`, `CDK_ADMIN_EMAIL` or `CDK_ADMIN_USER` for Console, `CDK_GATEWAY_USER` or `CDK_ADMIN_USER` for Gateway. Required if admin_password is set. If not provided and `mode` is Console, the API token will be used to authenticate.", }, - "gateway_cacert": schema.StringAttribute{ - Optional: true, - Description: "Root CA certificate in PEM format to verify the Conduktor Gateway certificate. May be set using environment variable `CDK_GATEWAY_CACERT`. If not provided, the system's root CA certificates will be used.", - MarkdownDescription: "Root CA certificate in PEM format to verify the Conduktor Gateway certificate. May be set using environment variable `CDK_GATEWAY_CACERT`. If not provided, the system's root CA certificates will be used.", - }, - "gateway_cert": schema.StringAttribute{ - Optional: true, - Description: "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_CERT`. Must be used with key. If key is provided, cert is required. Useful when Gateway is behind a reverse proxy with client certificate authentication.", - MarkdownDescription: "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_CERT`. Must be used with key. If key is provided, cert is required. Useful when Gateway is behind a reverse proxy with client certificate authentication.", - }, - "gateway_insecure": schema.BoolAttribute{ - Optional: true, - Description: "Skip TLS verification flag. May be set using environment variable `CDK_GATEWAY_INSECURE`.", - MarkdownDescription: "Skip TLS verification flag. May be set using environment variable `CDK_GATEWAY_INSECURE`.", - }, - "gateway_key": schema.StringAttribute{ + "api_token": schema.StringAttribute{ Optional: true, - Description: "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Gateway is behind a reverse proxy with client certificate authentication.", - MarkdownDescription: "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Gateway is behind a reverse proxy with client certificate authentication.", + Sensitive: true, + Description: "The API token to authenticate with the Conduktor Console API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_user and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information. Not used if `mode` is Gateway.", + MarkdownDescription: "The API token to authenticate with the Conduktor Console API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_user and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information. Not used if `mode` is Gateway.", }, - "gateway_password": schema.StringAttribute{ + "base_url": schema.StringAttribute{ Optional: true, - Sensitive: true, - Description: "The password of Gateway the admin user. May be set using environment variable `CDK_GATEWAY_PASSWORD`. Required if gateway_url is set.", - MarkdownDescription: "The password of Gateway the admin user. May be set using environment variable `CDK_GATEWAY_PASSWORD`. Required if gateway_url is set.", + Description: "The URL of either Conduktor Console or Gateway, depending on the `mode`. May be set using environment variable `CDK_CONSOLE_BASE_URL` or `CDK_BASE_URL` for Console, `CDK_GATEWAY_BASE_URL` or `CDK_BASE_URL` for Gateway. Required either here or in the environment.", + MarkdownDescription: "The URL of either Conduktor Console or Gateway, depending on the `mode`. May be set using environment variable `CDK_CONSOLE_BASE_URL` or `CDK_BASE_URL` for Console, `CDK_GATEWAY_BASE_URL` or `CDK_BASE_URL` for Gateway. Required either here or in the environment.", }, - "gateway_url": schema.StringAttribute{ + "cacert": schema.StringAttribute{ Optional: true, - Description: "The administration URL of the Conduktor Gateway. May also be set using environment variable `CDK_GATEWAY_BASE_URL`. Required either here or in the environment.", - MarkdownDescription: "The administration URL of the Conduktor Gateway. May also be set using environment variable `CDK_GATEWAY_BASE_URL`. Required either here or in the environment.", + Description: "Root CA certificate in PEM format to verify the Conduktor certificate. May be set using environment variable `CDK_CONSOLE_CACERT` or `CDK_CACERT` for Console, `CDK_GATEWAY_CACERT` or `CDK_CACERT` for Gateway. If not provided, the system's root CA certificates will be used.", + MarkdownDescription: "Root CA certificate in PEM format to verify the Conduktor certificate. May be set using environment variable `CDK_CONSOLE_CACERT` or `CDK_CACERT` for Console, `CDK_GATEWAY_CACERT` or `CDK_CACERT` for Gateway. If not provided, the system's root CA certificates will be used.", }, - "gateway_user": schema.StringAttribute{ + "cert": schema.StringAttribute{ Optional: true, - Description: "The login of a Gateway admin user. May be set using environment variable `CDK_GATEWAY_USER`. Required if gateway_url is set.", - MarkdownDescription: "The login of a Gateway admin user. May be set using environment variable `CDK_GATEWAY_USER`. Required if gateway_url is set.", + Description: "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_CERT` or `CDK_CERT` for Console, `CDK_GATEWAY_CERT` or `CDK_CERT` for Gateway. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication.", + MarkdownDescription: "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_CERT` or `CDK_CERT` for Console, `CDK_GATEWAY_CERT` or `CDK_CERT` for Gateway. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication.", }, "insecure": schema.BoolAttribute{ Optional: true, - Description: "Skip TLS verification flag. May be set using environment variable `CDK_INSECURE`.", - MarkdownDescription: "Skip TLS verification flag. May be set using environment variable `CDK_INSECURE`.", + Description: "Skip TLS verification flag. May be set using environment variable `CDK_CONSOLE_INSECURE` or `CDK_INSECURE` for Console, `CDK_GATEWAY_INSECURE` or `CDK_INSECURE` for Gateway.", + MarkdownDescription: "Skip TLS verification flag. May be set using environment variable `CDK_CONSOLE_INSECURE` or `CDK_INSECURE` for Console, `CDK_GATEWAY_INSECURE` or `CDK_INSECURE` for Gateway.", }, "key": schema.StringAttribute{ Optional: true, - Description: "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication.", - MarkdownDescription: "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication.", + Description: "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_KEY` or `CDK_KEY` for Console, `CDK_GATEWAY_KEY` or `CDK_KEY` for Gateway. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication.", + MarkdownDescription: "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_KEY` or `CDK_KEY` for Console, `CDK_GATEWAY_KEY` or `CDK_KEY` for Gateway. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication.", + }, + "mode": schema.StringAttribute{ + Required: true, + Description: "The mode that you would like to set for the terraform provider. May be set using environment variable `CDK_PROVIDER_MODE`. Can either be one of `console` or `gateway`", + MarkdownDescription: "The mode that you would like to set for the terraform provider. May be set using environment variable `CDK_PROVIDER_MODE`. Can either be one of `console` or `gateway`", + Validators: []validator.String{ + stringvalidator.OneOf(validation.ValidProviderMode...), + }, }, }, } } type ConduktorModel struct { - AdminEmail types.String `tfsdk:"admin_email"` - AdminPassword types.String `tfsdk:"admin_password"` - ApiToken types.String `tfsdk:"api_token"` - Cacert types.String `tfsdk:"cacert"` - Cert types.String `tfsdk:"cert"` - ConsoleUrl types.String `tfsdk:"console_url"` - GatewayCacert types.String `tfsdk:"gateway_cacert"` - GatewayCert types.String `tfsdk:"gateway_cert"` - GatewayInsecure types.Bool `tfsdk:"gateway_insecure"` - GatewayKey types.String `tfsdk:"gateway_key"` - GatewayPassword types.String `tfsdk:"gateway_password"` - GatewayUrl types.String `tfsdk:"gateway_url"` - GatewayUser types.String `tfsdk:"gateway_user"` - Insecure types.Bool `tfsdk:"insecure"` - Key types.String `tfsdk:"key"` + AdminPassword types.String `tfsdk:"admin_password"` + AdminUser types.String `tfsdk:"admin_user"` + ApiToken types.String `tfsdk:"api_token"` + BaseUrl types.String `tfsdk:"base_url"` + Cacert types.String `tfsdk:"cacert"` + Cert types.String `tfsdk:"cert"` + Insecure types.Bool `tfsdk:"insecure"` + Key types.String `tfsdk:"key"` + Mode types.String `tfsdk:"mode"` } diff --git a/internal/schema/validation/schema_validation.go b/internal/schema/validation/schema_validation.go index 7e31124..a4c952a 100644 --- a/internal/schema/validation/schema_validation.go +++ b/internal/schema/validation/schema_validation.go @@ -43,6 +43,10 @@ var ValidPermissions = []string{ "topicAddPartition", "topicDelete", } + +// Provider modes. +var ValidProviderMode = []string{"console", "gateway"} + var ValidPermissionTypes = []string{"CLUSTER", "CONSUMER_GROUP", "KAFKA_CONNECT", "KSQLDB", "PLATFORM", "SUBJECT", "TOPIC"} var ValidPermissionPatternTypes = []string{"LITERAL", "PREFIXED"} diff --git a/provider_code_spec.json b/provider_code_spec.json index 3ce52fb..a3dd389 100644 --- a/provider_code_spec.json +++ b/provider_code_spec.json @@ -4,31 +4,53 @@ "schema": { "attributes": [ { - "name": "console_url", + "name": "mode", "string": { - "description": "The URL of the Conduktor Console. May be set using environment variable `CDK_BASE_URL` or `CDK_CONSOLE_URL`. Required either here or in the environment.", + "description": "The mode that you would like to set for the terraform provider. May be set using environment variable `CDK_PROVIDER_MODE`. Can either be one of `console` or `gateway`", + "optional_required": "required", + "validators": [ + { + "custom": { + "imports": [ + { + "path": "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + }, + { + "path": "github.com/conduktor/terraform-provider-conduktor/internal/schema/validation" + } + ], + "schema_definition": "stringvalidator.OneOf(validation.ValidProviderMode...)" + } + } + ] + } + }, + { + "name": "base_url", + "string": { + "description": "The URL of either Conduktor Console or Gateway, depending on the `mode`. May be set using environment variable `CDK_CONSOLE_BASE_URL` or `CDK_BASE_URL` for Console, `CDK_GATEWAY_BASE_URL` or `CDK_BASE_URL` for Gateway. Required either here or in the environment.", "optional_required": "optional" } }, { "name": "api_token", "string": { - "description": "The API token to authenticate with the Conduktor API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_email and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information.", + "description": "The API token to authenticate with the Conduktor Console API. May be set using environment variable `CDK_API_TOKEN` or `CDK_API_KEY`. If not provided, admin_user and admin_password will be used to authenticate. See [documentation](https://docs.conduktor.io/platform/reference/api-reference/#generate-an-api-key) for more information. Not used if `mode` is Gateway.", "optional_required": "optional", "sensitive": true } }, { - "name": "admin_email", + "name": "admin_user", "string": { - "description": "The email of the admin user. May be set using environment variable `CDK_ADMIN_EMAIL`. Required if admin_password is set. If not provided, the API token will be used to authenticate.", + "description": "The login credentials of the admin user. May be set using environment variable `CDK_CONSOLE_USER`, `CDK_ADMIN_EMAIL` or `CDK_ADMIN_USER` for Console, `CDK_GATEWAY_USER` or `CDK_ADMIN_USER` for Gateway. Required if admin_password is set. If not provided and `mode` is Console, the API token will be used to authenticate.", "optional_required": "optional" } }, { "name": "admin_password", "string": { - "description": "The password of the admin user. May be set using environment variable `CDK_ADMIN_PASSWORD`. Required if admin_email is set. If not provided, the API token will be used to authenticater.", + "description": "The password of the admin user. May be set using environment variable `CDK_CONSOLE_PASSWORD` or `CDK_ADMIN_PASSWORD` for Console, `CDK_GATEWAY_PASSWORD` or `CDK_ADMIN_PASSWORD` for Gateway. Required if admin_email is set. If not provided, the API token will be used to authenticater.", "optional_required": "optional", "sensitive": true } @@ -36,78 +58,28 @@ { "name": "cacert", "string": { - "description": "Root CA certificate in PEM format to verify the Conduktor Console certificate. May be set using environment variable `CDK_CACERT`. If not provided, the system's root CA certificates will be used.", + "description": "Root CA certificate in PEM format to verify the Conduktor certificate. May be set using environment variable `CDK_CONSOLE_CACERT` or `CDK_CACERT` for Console, `CDK_GATEWAY_CACERT` or `CDK_CACERT` for Gateway. If not provided, the system's root CA certificates will be used.", "optional_required": "optional" } }, { "name": "insecure", "bool": { - "description": "Skip TLS verification flag. May be set using environment variable `CDK_INSECURE`.", + "description": "Skip TLS verification flag. May be set using environment variable `CDK_CONSOLE_INSECURE` or `CDK_INSECURE` for Console, `CDK_GATEWAY_INSECURE` or `CDK_INSECURE` for Gateway.", "optional_required": "optional" } }, { "name": "cert", "string": { - "description": "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CERT`. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication.", + "description": "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_CERT` or `CDK_CERT` for Console, `CDK_GATEWAY_CERT` or `CDK_CERT` for Gateway. Must be used with key. If key is provided, cert is required. Useful when Console is behind a reverse proxy with client certificate authentication.", "optional_required": "optional" } }, { "name": "key", "string": { - "description": "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication.", - "optional_required": "optional" - } - }, - { - "name": "gateway_url", - "string": { - "description": "The administration URL of the Conduktor Gateway. May also be set using environment variable `CDK_GATEWAY_BASE_URL`. Required either here or in the environment.", - "optional_required": "optional" - } - }, - { - "name": "gateway_user", - "string": { - "description": "The login of a Gateway admin user. May be set using environment variable `CDK_GATEWAY_USER`. Required if gateway_url is set.", - "optional_required": "optional" - } - }, - { - "name": "gateway_password", - "string": { - "description": "The password of Gateway the admin user. May be set using environment variable `CDK_GATEWAY_PASSWORD`. Required if gateway_url is set.", - "optional_required": "optional", - "sensitive": true - } - }, - { - "name": "gateway_cacert", - "string": { - "description": "Root CA certificate in PEM format to verify the Conduktor Gateway certificate. May be set using environment variable `CDK_GATEWAY_CACERT`. If not provided, the system's root CA certificates will be used.", - "optional_required": "optional" - } - }, - { - "name": "gateway_insecure", - "bool": { - "description": "Skip TLS verification flag. May be set using environment variable `CDK_GATEWAY_INSECURE`.", - "optional_required": "optional" - } - }, - { - "name": "gateway_cert", - "string": { - "description": "Cert in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_CERT`. Must be used with key. If key is provided, cert is required. Useful when Gateway is behind a reverse proxy with client certificate authentication.", - "optional_required": "optional" - } - }, - { - "name": "gateway_key", - "string": { - "description": "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_GATEWAY_KEY`. Must be used with cert. If cert is provided, key is required. Useful when Gateway is behind a reverse proxy with client certificate authentication.", + "description": "Key in PEM format to authenticate using client certificates. May be set using environment variable `CDK_CONSOLE_KEY` or `CDK_KEY` for Console, `CDK_GATEWAY_KEY` or `CDK_KEY` for Gateway. Must be used with cert. If cert is provided, key is required. Useful when Console is behind a reverse proxy with client certificate authentication.", "optional_required": "optional" } } diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index d519e72..0bbc32c 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -16,6 +16,16 @@ The Conduktor provider is used to interact with the resources supported by Condu ## Example Usage -{{tffile "examples/provider/provider.tf"}} +### Console client only + +{{tffile "examples/provider/console_provider.tf"}} + +### Gateway client only + +{{tffile "examples/provider/gateway_provider.tf"}} + +### Multi client configuration using [terraform alias](https://developer.hashicorp.com/terraform/language/providers/configuration#alias-multiple-provider-configurations) + +{{tffile "examples/provider/multi_provider.tf"}} {{ .SchemaMarkdown }}