From 1b883a38ec4a6279497c47ddc9954c3c53619b78 Mon Sep 17 00:00:00 2001 From: Darek Stopka Date: Mon, 10 Jul 2023 14:51:06 +0000 Subject: [PATCH 1/3] DXE-2842 Reinstate env variables config support and fix config block signature error --- CHANGELOG.md | 7 + pkg/akamai/configure_context.go | 19 +-- pkg/akamai/edgegrid.go | 75 +++++++-- pkg/akamai/edgegrid_test.go | 261 +++++++++++++++++++++++++++---- pkg/akamai/framework_provider.go | 52 +++--- pkg/akamai/plugin_provider.go | 40 ++--- 6 files changed, 354 insertions(+), 100 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fdd56a67..b9402ff58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # RELEASE NOTES +## 5.0.1 (Jul 12, 2023) + +#### BUG FIXES: + +* Reinstated support for configuring provider with environmental variables ([#407](https://github.com/akamai/terraform-provider-akamai/issues/407), [#444](https://github.com/akamai/terraform-provider-akamai/issues/444)) +* Fixed `signature does not match` error when using `config` block for authentication ([#444](https://github.com/akamai/terraform-provider-akamai/issues/444), [#446](https://github.com/akamai/terraform-provider-akamai/issues/446)) + ## 5.0.0 (Jul 5, 2023) #### BREAKING CHANGES: diff --git a/pkg/akamai/configure_context.go b/pkg/akamai/configure_context.go index fdb7ef75a..1a79e547d 100644 --- a/pkg/akamai/configure_context.go +++ b/pkg/akamai/configure_context.go @@ -14,26 +14,19 @@ import ( ) type contextConfig struct { - edgercPath string - edgercSection string - edgercConfig *edgegrid.Config - userAgent string - ctx context.Context - requestLimit int - enableCache bool + edgegridConfig *edgegrid.Config + userAgent string + ctx context.Context + requestLimit int + enableCache bool } func configureContext(cfg contextConfig) (*meta.OperationMeta, error) { operationID := uuid.NewString() log := logger.FromContext(cfg.ctx, "OperationID", operationID) - edgerc, err := newEdgegridConfig(cfg.edgercPath, cfg.edgercSection, cfg.edgercConfig) - if err != nil { - return nil, err - } - sess, err := session.New( - session.WithSigner(edgerc), + session.WithSigner(cfg.edgegridConfig), session.WithUserAgent(cfg.userAgent), session.WithLog(log), session.WithHTTPTracing(cast.ToBool(os.Getenv("AKAMAI_HTTP_TRACE_ENABLED"))), diff --git a/pkg/akamai/edgegrid.go b/pkg/akamai/edgegrid.go index 57c8be604..dd9bc414b 100644 --- a/pkg/akamai/edgegrid.go +++ b/pkg/akamai/edgegrid.go @@ -10,22 +10,71 @@ import ( // ErrWrongEdgeGridConfiguration is returned when the configuration could not be read var ErrWrongEdgeGridConfiguration = errors.New("error reading Akamai EdgeGrid configuration") -func newEdgegridConfig(path, section string, config *edgegrid.Config) (*edgegrid.Config, error) { - if (path != "" || section != "") && config != nil { - return nil, fmt.Errorf("edgegrid cannot be simultaneously configured with file and config map") // should not happen as schema guarantees that +var defaultConfigFile = edgegrid.DefaultConfigFile + +type configBearer struct { + accessToken string + accountKey string + clientSecret string + clientToken string + host string + maxBody int +} + +func (c configBearer) toEdgegridConfig() (*edgegrid.Config, error) { + if !c.valid() { + return nil, ErrWrongEdgeGridConfiguration + } + + edgerc := &edgegrid.Config{ + AccessToken: c.accessToken, + AccountKey: c.accountKey, + ClientSecret: c.clientSecret, + ClientToken: c.clientToken, + Host: c.host, + MaxBody: c.maxBody, } - var edgerc *edgegrid.Config - if config != nil { - edgerc = config - } else { - edgerc = &edgegrid.Config{} - err := edgerc.FromFile(edgercPathOrDefault(path), edgercSectionOrDefault(section)) - if err != nil { - return nil, fmt.Errorf("%w: %s", ErrWrongEdgeGridConfiguration, err) - } + if edgerc.MaxBody == 0 { + edgerc.MaxBody = edgegrid.MaxBodySize } + return edgerc, nil +} + +func (c configBearer) valid() bool { + return c.host != "" && c.accessToken != "" && c.clientSecret != "" && c.clientToken != "" +} + +// newEdgegridConfig creates a new edgegrid.Config based on provided arguments. +// +// It evaluates possibility of creating the config in the following order: +// 1. Environmental variables +// 2. Config block +// 3. Edgerc file +// +// If edgerc path or section are not provided, it uses the edgegrid defaults. +func newEdgegridConfig(path, section string, config configBearer) (*edgegrid.Config, error) { + envEdgerc := &edgegrid.Config{} + err := envEdgerc.FromEnv(edgercSectionOrDefault(section)) + if err == nil { + return validateEdgerc(envEdgerc) + } + + configEdgerc, err := config.toEdgegridConfig() + if err == nil { + return validateEdgerc(configEdgerc) + } + + fileEdgerc := &edgegrid.Config{} + err = fileEdgerc.FromFile(edgercPathOrDefault(path), edgercSectionOrDefault(section)) + if err == nil { + return validateEdgerc(fileEdgerc) + } + return nil, fmt.Errorf("%w: %s", ErrWrongEdgeGridConfiguration, err) +} + +func validateEdgerc(edgerc *edgegrid.Config) (*edgegrid.Config, error) { if err := edgerc.Validate(); err != nil { return nil, fmt.Errorf("%w: %s", ErrWrongEdgeGridConfiguration, err) } @@ -35,7 +84,7 @@ func newEdgegridConfig(path, section string, config *edgegrid.Config) (*edgegrid func edgercPathOrDefault(path string) string { if path == "" { - return edgegrid.DefaultConfigFile + return defaultConfigFile } return path } diff --git a/pkg/akamai/edgegrid_test.go b/pkg/akamai/edgegrid_test.go index f29c28350..88fa5d8bf 100644 --- a/pkg/akamai/edgegrid_test.go +++ b/pkg/akamai/edgegrid_test.go @@ -1,6 +1,7 @@ package akamai import ( + "fmt" "testing" "github.com/akamai/AkamaiOPEN-edgegrid-golang/v7/pkg/edgegrid" @@ -9,54 +10,252 @@ import ( ) func TestNewEdgegridConfig(t *testing.T) { - t.Parallel() - - path := "testdata/edgerc" + edgercPath := "testdata/edgerc" section := "default" - config := func() *edgegrid.Config { - return &edgegrid.Config{ - Host: "host.com", - AccessToken: "access_token", - ClientToken: "client_token", - ClientSecret: "client_secret", - MaxBody: 0, - AccountKey: "", - } + clientSecret := "test_client_secret" + clientToken := "test_client_token" + accessToken := "test_access_token" + + envHost := "env.com" + configHost := "config.com" + fileHost := "host.com" + + config := configBearer{ + host: configHost, + accessToken: accessToken, + clientToken: clientToken, + clientSecret: clientSecret, } - t.Run("from config map", func(t *testing.T) { - t.Parallel() + defer func(oldDefault string) { + defaultConfigFile = oldDefault + }(defaultConfigFile) + defaultConfigFile = edgercPath + + t.Run("env is prioritized over config", func(t *testing.T) { + t.Setenv("AKAMAI_HOST", envHost) + t.Setenv("AKAMAI_ACCESS_TOKEN", accessToken) + t.Setenv("AKAMAI_CLIENT_TOKEN", clientToken) + t.Setenv("AKAMAI_CLIENT_SECRET", clientSecret) - _, err := newEdgegridConfig("", "", config()) + edgegridConfig, err := newEdgegridConfig("", "", config) require.NoError(t, err) + assert.Equal(t, envHost, edgegridConfig.Host) }) - t.Run("from file", func(t *testing.T) { - t.Parallel() + t.Run("env is prioritized over file", func(t *testing.T) { + t.Setenv("AKAMAI_HOST", envHost) + t.Setenv("AKAMAI_ACCESS_TOKEN", accessToken) + t.Setenv("AKAMAI_CLIENT_TOKEN", clientToken) + t.Setenv("AKAMAI_CLIENT_SECRET", clientSecret) - _, err := newEdgegridConfig(path, section, nil) + edgegridConfig, err := newEdgegridConfig(edgercPath, section, configBearer{}) require.NoError(t, err) + assert.Equal(t, envHost, edgegridConfig.Host) }) - t.Run("invalid arguments", func(t *testing.T) { - t.Parallel() + t.Run("non-default section is used for reading env", func(t *testing.T) { + testSection := "TEST" + host := "testenv.com" - _, err := newEdgegridConfig(path, "", config()) - assert.Error(t, err) + t.Setenv(fmt.Sprintf("AKAMAI_%s_HOST", testSection), host) + t.Setenv(fmt.Sprintf("AKAMAI_%s_ACCESS_TOKEN", testSection), accessToken) + t.Setenv(fmt.Sprintf("AKAMAI_%s_CLIENT_TOKEN", testSection), clientToken) + t.Setenv(fmt.Sprintf("AKAMAI_%s_CLIENT_SECRET", testSection), clientSecret) - _, err = newEdgegridConfig("", section, config()) - assert.Error(t, err) + edgegridConfig, err := newEdgegridConfig("", testSection, config) + require.NoError(t, err) + assert.Equal(t, host, edgegridConfig.Host) + }) - _, err = newEdgegridConfig(path, section, config()) - assert.Error(t, err) + t.Run("uses config when provided and env not set", func(t *testing.T) { + edgegridConfig, err := newEdgegridConfig("", "", config) + require.NoError(t, err) + assert.Equal(t, configHost, edgegridConfig.Host) + }) + + t.Run("uses config when provided and env not valid", func(t *testing.T) { + t.Setenv("AKAMAI_HOST", "env.com") + t.Setenv("AKAMAI_ACCESS_TOKEN", accessToken) + + edgegridConfig, err := newEdgegridConfig("", "", config) + require.NoError(t, err) + assert.Equal(t, configHost, edgegridConfig.Host) }) - t.Run("validate fail", func(t *testing.T) { - t.Parallel() + t.Run("config is prioritized over edgerc file", func(t *testing.T) { + edgegridConfig, err := newEdgegridConfig(edgercPath, section, config) + require.NoError(t, err) + assert.Equal(t, configHost, edgegridConfig.Host) + }) + + t.Run("uses edgerc file when env and config not provided", func(t *testing.T) { + edgegridConfig, err := newEdgegridConfig(edgercPath, section, configBearer{}) + require.NoError(t, err) + assert.Equal(t, fileHost, edgegridConfig.Host) + }) + + t.Run("uses edgerc file when provided and env is invalid", func(t *testing.T) { + t.Setenv("AKAMAI_HOST", "env.com") + t.Setenv("AKAMAI_ACCESS_TOKEN", accessToken) + + edgegridConfig, err := newEdgegridConfig(edgercPath, section, configBearer{}) + require.NoError(t, err) + assert.Equal(t, fileHost, edgegridConfig.Host) + + }) + + t.Run("uses default edgerc path and section when none provided", func(t *testing.T) { + edgegridConfig, err := newEdgegridConfig("", "", configBearer{}) + require.NoError(t, err) + assert.Equal(t, fileHost, edgegridConfig.Host) + }) +} + +func TestEdgercPathOrDefault(t *testing.T) { + t.Parallel() + + path := "testdata/edgerc" + + assert.Equal(t, path, edgercPathOrDefault(path)) + assert.Equal(t, defaultConfigFile, edgercPathOrDefault("")) +} + +func TestEdgercSectionOrDefault(t *testing.T) { + t.Parallel() + + section := "not_default" + + assert.Equal(t, section, edgercSectionOrDefault(section)) + assert.Equal(t, edgegrid.DefaultSection, edgercSectionOrDefault("")) +} - cfg := config() - cfg.Host = "host.com/" - _, err := newEdgegridConfig("", "", cfg) +func TestConfigBearerToEdgegridConfig(t *testing.T) { + t.Parallel() + + accessToken := "test_access_token" + accountKey := "test_account_key" + clientSecret := "test_client_secret" + clientToken := "test_client_token" + host := "host.com" + maxBody := 1234 + + t.Run("returns error when config not valid", func(t *testing.T) { + configBearer := configBearer{ + host: host, + accessToken: accessToken, + } + + _, err := configBearer.toEdgegridConfig() assert.Error(t, err) }) + t.Run("creates edgegrid.Config using all values", func(t *testing.T) { + configBearer := configBearer{ + accessToken: accessToken, + accountKey: accountKey, + clientSecret: clientSecret, + clientToken: clientToken, + host: host, + maxBody: maxBody, + } + + edgegridConfig, err := configBearer.toEdgegridConfig() + require.NoError(t, err) + + assert.Equal(t, configBearer.accessToken, edgegridConfig.AccessToken) + assert.Equal(t, configBearer.accountKey, edgegridConfig.AccountKey) + assert.Equal(t, configBearer.clientSecret, edgegridConfig.ClientSecret) + assert.Equal(t, configBearer.clientToken, edgegridConfig.ClientToken) + assert.Equal(t, configBearer.host, edgegridConfig.Host) + assert.Equal(t, configBearer.maxBody, edgegridConfig.MaxBody) + }) + t.Run("sets MaxBody to MaxBodySize when 0", func(t *testing.T) { + configBearer := configBearer{ + accessToken: accessToken, + clientSecret: clientSecret, + clientToken: clientToken, + host: host, + } + + edgegridConfig, err := configBearer.toEdgegridConfig() + require.NoError(t, err) + + assert.Equal(t, edgegrid.MaxBodySize, edgegridConfig.MaxBody) + }) +} + +func TestConfigBearerValid(t *testing.T) { + t.Parallel() + + accessToken := "test_access_token" + clientSecret := "test_client_secret" + clientToken := "test_client_token" + host := "host.com" + + testCases := map[string]struct { + config configBearer + expected bool + }{ + "is valid when only required provided": { + config: configBearer{ + accessToken: accessToken, + clientSecret: clientSecret, + clientToken: clientToken, + host: host, + }, + expected: true, + }, + "is valid when all attributes provided": { + config: configBearer{ + accessToken: accessToken, + clientSecret: clientSecret, + clientToken: clientToken, + host: host, + maxBody: 1234, + accountKey: "test_account_key", + }, + expected: true, + }, + "not valid - empty host": { + config: configBearer{ + accessToken: accessToken, + clientSecret: clientSecret, + clientToken: clientToken, + }, + expected: false, + }, + "not valid - empty client token": { + config: configBearer{ + accessToken: accessToken, + clientSecret: clientSecret, + host: host, + }, + expected: false, + }, + "not valid - empty client secret": { + config: configBearer{ + accessToken: accessToken, + clientToken: clientToken, + host: host, + }, + expected: false, + }, + "not valid - empty access token": { + config: configBearer{ + clientSecret: clientSecret, + clientToken: clientToken, + host: host, + }, + expected: false, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + + t.Run(name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tc.expected, tc.config.valid()) + }) + } } diff --git a/pkg/akamai/framework_provider.go b/pkg/akamai/framework_provider.go index 0c94b8f76..04b64321f 100644 --- a/pkg/akamai/framework_provider.go +++ b/pkg/akamai/framework_provider.go @@ -5,7 +5,6 @@ import ( "os" "strconv" - "github.com/akamai/AkamaiOPEN-edgegrid-golang/v7/pkg/edgegrid" "github.com/akamai/terraform-provider-akamai/v5/pkg/config" "github.com/akamai/terraform-provider-akamai/v5/pkg/subprovider" "github.com/akamai/terraform-provider-akamai/v5/version" @@ -33,8 +32,8 @@ type ProviderModel struct { RequestLimit types.Int64 `tfsdk:"request_limit"` } -// EdgegridConfigModel represents the model of edgegrid configuration block -type EdgegridConfigModel struct { +// ConfigModel represents the model of edgegrid configuration block +type ConfigModel struct { Host types.String `tfsdk:"host"` AccessToken types.String `tfsdk:"access_token"` ClientToken types.String `tfsdk:"client_token"` @@ -117,32 +116,37 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest, } } - var edgercConfigModels []EdgegridConfigModel - resp.Diagnostics.Append(data.EdgercConfig.ElementsAs(ctx, &edgercConfigModels, false)...) - if resp.Diagnostics.HasError() { - return + var edgegridConfigBearer configBearer + if !data.EdgercConfig.IsNull() { + var configModels []ConfigModel + resp.Diagnostics.Append(data.EdgercConfig.ElementsAs(ctx, &configModels, false)...) + if resp.Diagnostics.HasError() { + return + } + configModel := configModels[0] + edgegridConfigBearer = configBearer{ + accessToken: configModel.AccessToken.ValueString(), + accountKey: configModel.AccountKey.ValueString(), + clientSecret: configModel.ClientSecret.ValueString(), + clientToken: configModel.ClientToken.ValueString(), + host: configModel.Host.ValueString(), + maxBody: int(configModel.MaxBody.ValueInt64()), + } + } - var edgercConfig *edgegrid.Config - if len(edgercConfigModels) > 0 { - edgercConfig = &edgegrid.Config{ - Host: edgercConfigModels[0].Host.ValueString(), - ClientToken: edgercConfigModels[0].ClientToken.ValueString(), - ClientSecret: edgercConfigModels[0].ClientSecret.ValueString(), - AccessToken: edgercConfigModels[0].AccessToken.ValueString(), - AccountKey: edgercConfigModels[0].AccountKey.ValueString(), - MaxBody: int(edgercConfigModels[0].MaxBody.ValueInt64()), - } + edgegridConfig, err := newEdgegridConfig(data.EdgercPath.ValueString(), data.EdgercSection.ValueString(), edgegridConfigBearer) + if err != nil { + resp.Diagnostics.Append(diag.NewErrorDiagnostic("configuring context failed", err.Error())) + return } meta, err := configureContext(contextConfig{ - edgercPath: data.EdgercPath.ValueString(), - edgercSection: data.EdgercSection.ValueString(), - edgercConfig: edgercConfig, - userAgent: userAgent(req.TerraformVersion), - ctx: ctx, - requestLimit: int(data.RequestLimit.ValueInt64()), - enableCache: data.CacheEnabled.ValueBool(), + edgegridConfig: edgegridConfig, + userAgent: userAgent(req.TerraformVersion), + ctx: ctx, + requestLimit: int(data.RequestLimit.ValueInt64()), + enableCache: data.CacheEnabled.ValueBool(), }) if err != nil { resp.Diagnostics.Append(diag.NewErrorDiagnostic("configuring context failed", err.Error())) diff --git a/pkg/akamai/plugin_provider.go b/pkg/akamai/plugin_provider.go index cfcb6292b..a55e242f3 100644 --- a/pkg/akamai/plugin_provider.go +++ b/pkg/akamai/plugin_provider.go @@ -11,7 +11,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" - "github.com/akamai/AkamaiOPEN-edgegrid-golang/v7/pkg/edgegrid" "github.com/akamai/terraform-provider-akamai/v5/pkg/cache" "github.com/akamai/terraform-provider-akamai/v5/pkg/common/collections" "github.com/akamai/terraform-provider-akamai/v5/pkg/common/tf" @@ -96,27 +95,32 @@ func configureProviderContext(p *schema.Provider) schema.ConfigureContextFunc { return nil, diag.FromErr(err) } - envs, err := tf.GetSetValue("config", d) + configSet, err := tf.GetSetValue("config", d) if err != nil && !errors.Is(err, tf.ErrNotFound) { return nil, diag.FromErr(err) } - var edgercConfig *edgegrid.Config - if err == nil && len(envs.List()) > 0 { - envsMap, ok := envs.List()[0].(map[string]any) + var edgegridConfigBearer configBearer + if configSet.Len() > 0 { + configMap, ok := configSet.List()[0].(map[string]any) if !ok { return nil, diag.FromErr(fmt.Errorf("%w: %s, %q", tf.ErrInvalidType, "config", "map[string]any")) } - edgercConfig = &edgegrid.Config{ - Host: envsMap["host"].(string), - ClientToken: envsMap["client_token"].(string), - ClientSecret: envsMap["client_secret"].(string), - AccessToken: envsMap["access_token"].(string), - AccountKey: envsMap["account_key"].(string), - MaxBody: envsMap["max_body"].(int), + edgegridConfigBearer = configBearer{ + accessToken: configMap["access_token"].(string), + accountKey: configMap["account_key"].(string), + clientSecret: configMap["client_secret"].(string), + clientToken: configMap["client_token"].(string), + host: configMap["host"].(string), + maxBody: configMap["max_body"].(int), } } + edgegridConfig, err := newEdgegridConfig(edgercPath, edgercSection, edgegridConfigBearer) + if err != nil { + return nil, diag.FromErr(err) + } + requestLimit, err := tf.GetIntValue("request_limit", d) if err != nil { if !errors.Is(err, tf.ErrNotFound) { @@ -131,13 +135,11 @@ func configureProviderContext(p *schema.Provider) schema.ConfigureContextFunc { } meta, err := configureContext(contextConfig{ - edgercPath: edgercPath, - edgercSection: edgercSection, - edgercConfig: edgercConfig, - userAgent: userAgent(p.TerraformVersion), - ctx: ctx, - requestLimit: requestLimit, - enableCache: cacheEnabled, + edgegridConfig: edgegridConfig, + userAgent: userAgent(p.TerraformVersion), + ctx: ctx, + requestLimit: requestLimit, + enableCache: cacheEnabled, }) if err != nil { return nil, diag.FromErr(err) From cc4a7e7e1c3c8445b2a9657ad850bfb48c30dc52 Mon Sep 17 00:00:00 2001 From: "Zagrajczuk, Wojciech" Date: Mon, 10 Jul 2023 17:00:51 +0200 Subject: [PATCH 2/3] DXE-2849 Update version for the release --- docs/guides/get-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/get-started.md b/docs/guides/get-started.md index 499a44578..2322070ab 100644 --- a/docs/guides/get-started.md +++ b/docs/guides/get-started.md @@ -21,7 +21,7 @@ Your Akamai Terraform configuration starts with listing us as a required provide required_providers { akamai = { source = "akamai/akamai" - version = "5.0.0" + version = "5.0.1" } } } From a3bda571030b401bb3a4d4e0522c622268209db8 Mon Sep 17 00:00:00 2001 From: Michal Wojcik Date: Wed, 12 Jul 2023 07:09:53 +0000 Subject: [PATCH 3/3] DXE-2857 TFP goreleaser builds are failing --- .github/workflows/release.yml | 2 +- build/internal/releaser/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 64e2ebcb0..27be0553b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: - version: latest + version: v1.18.2 args: release --rm-dist env: GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} diff --git a/build/internal/releaser/Dockerfile b/build/internal/releaser/Dockerfile index 27c600068..cf2e5cd11 100644 --- a/build/internal/releaser/Dockerfile +++ b/build/internal/releaser/Dockerfile @@ -22,6 +22,6 @@ RUN apt update && apt install -y curl git gcc ca-certificates openssh-client gnu && update-ca-certificates \ && curl -o go1.18.linux-amd64.tar.gz https://dl.google.com/go/go1.18.linux-amd64.tar.gz \ && rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz \ - && go install github.com/goreleaser/goreleaser@latest \ + && go install github.com/goreleaser/goreleaser@v1.18.2 \ && mkdir -p /root/.terraform.d/plugins/registry.terraform.io/akamai/akamai/10.0.0/linux_amd64 /root/.ssh