Make backend.ConfigSchema accept a context (#776)

Signed-off-by: Marcin Wyszynski <marcin.pixie@gmail.com>
This commit is contained in:
Marcin Wyszynski 2023-10-24 13:14:01 +02:00 committed by GitHub
parent a6ebabfea6
commit bda32938e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 79 additions and 33 deletions

View File

@ -61,7 +61,7 @@ type Backend interface {
// //
// This method does not have any side-effects for the backend and can // This method does not have any side-effects for the backend and can
// be safely used before configuring. // be safely used before configuring.
ConfigSchema() *configschema.Block ConfigSchema(context.Context) *configschema.Block
// PrepareConfig checks the validity of the values in the given // PrepareConfig checks the validity of the values in the given
// configuration, and inserts any missing defaults, assuming that its // configuration, and inserts any missing defaults, assuming that its

View File

@ -104,9 +104,9 @@ func NewWithBackend(backend backend.Backend) *Local {
} }
} }
func (b *Local) ConfigSchema() *configschema.Block { func (b *Local) ConfigSchema(ctx context.Context) *configschema.Block {
if b.Backend != nil { if b.Backend != nil {
return b.Backend.ConfigSchema() return b.Backend.ConfigSchema(ctx)
} }
return &configschema.Block{ return &configschema.Block{
Attributes: map[string]*configschema.Attribute{ Attributes: map[string]*configschema.Attribute{

View File

@ -4,6 +4,7 @@
package local package local
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -216,7 +217,7 @@ func (b backendWithStateStorageThatFailsRefresh) StateMgr(workspace string) (sta
return &stateStorageThatFailsRefresh{}, nil return &stateStorageThatFailsRefresh{}, nil
} }
func (b backendWithStateStorageThatFailsRefresh) ConfigSchema() *configschema.Block { func (b backendWithStateStorageThatFailsRefresh) ConfigSchema(context.Context) *configschema.Block {
return &configschema.Block{} return &configschema.Block{}
} }

View File

@ -7,6 +7,7 @@ package pg
// TF_ACC=1 GO111MODULE=on go test -v -mod=vendor -timeout=2m -parallel=4 github.com/opentofu/opentofu/backend/remote-state/pg // TF_ACC=1 GO111MODULE=on go test -v -mod=vendor -timeout=2m -parallel=4 github.com/opentofu/opentofu/backend/remote-state/pg
import ( import (
"context"
"database/sql" "database/sql"
"fmt" "fmt"
"net/url" "net/url"
@ -149,9 +150,11 @@ func TestBackendConfig(t *testing.T) {
} }
defer dbCleaner.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName)) defer dbCleaner.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName))
ctx := context.Background()
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
b := New().(*Backend) b := New().(*Backend)
schema := b.ConfigSchema() schema := b.ConfigSchema(ctx)
spec := schema.DecoderSpec() spec := schema.DecoderSpec()
obj, decDiags := hcldec.Decode(config, spec, nil) obj, decDiags := hcldec.Decode(config, spec, nil)
diags = diags.Append(decDiags) diags = diags.Append(decDiags)

View File

@ -48,7 +48,7 @@ type Backend struct {
// ConfigSchema returns a description of the expected configuration // ConfigSchema returns a description of the expected configuration
// structure for the receiving backend. // structure for the receiving backend.
func (b *Backend) ConfigSchema() *configschema.Block { func (b *Backend) ConfigSchema(context.Context) *configschema.Block {
return &configschema.Block{ return &configschema.Block{
Attributes: map[string]*configschema.Attribute{ Attributes: map[string]*configschema.Attribute{
"bucket": { "bucket": {

View File

@ -45,7 +45,7 @@ func ExpectDiagsEqual(expected tfdiags.Diagnostics) diagsValidator {
type diagsValidator func(*testing.T, tfdiags.Diagnostics) type diagsValidator func(*testing.T, tfdiags.Diagnostics)
// ExpectDiagsMatching returns a validator expeceting a single Diagnostic with fields matching the expectation // ExpectDiagsMatching returns a validator expecting a single Diagnostic with fields matching the expectation
func ExpectDiagsMatching(severity tfdiags.Severity, summary matcher, detail matcher) diagsValidator { func ExpectDiagsMatching(severity tfdiags.Severity, summary matcher, detail matcher) diagsValidator {
return func(t *testing.T, diags tfdiags.Diagnostics) { return func(t *testing.T, diags tfdiags.Diagnostics) {
for _, d := range diags { for _, d := range diags {
@ -1841,7 +1841,9 @@ func setSharedConfigFile(filename string) {
func configureBackend(t *testing.T, config map[string]any) (*Backend, tfdiags.Diagnostics) { func configureBackend(t *testing.T, config map[string]any) (*Backend, tfdiags.Diagnostics) {
b := New().(*Backend) b := New().(*Backend)
configSchema := populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config)) ctx := context.Background()
configSchema := populateSchema(t, b.ConfigSchema(ctx), hcl2shim.HCL2ValueFromConfigValue(config))
configSchema, diags := b.PrepareConfig(configSchema) configSchema, diags := b.PrepareConfig(configSchema)

View File

@ -127,9 +127,11 @@ func TestBackendConfig_InvalidRegion(t *testing.T) {
} }
for name, tc := range cases { for name, tc := range cases {
ctx := context.Background()
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
b := New() b := New()
configSchema := populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(tc.config)) configSchema := populateSchema(t, b.ConfigSchema(ctx), hcl2shim.HCL2ValueFromConfigValue(tc.config))
configSchema, diags := b.PrepareConfig(configSchema) configSchema, diags := b.PrepareConfig(configSchema)
if len(diags) > 0 { if len(diags) > 0 {
@ -365,8 +367,10 @@ func TestBackendConfig_STSEndpoint(t *testing.T) {
config["sts_endpoint"] = endpoint config["sts_endpoint"] = endpoint
} }
ctx := context.Background()
b := New() b := New()
configSchema := populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config)) configSchema := populateSchema(t, b.ConfigSchema(ctx), hcl2shim.HCL2ValueFromConfigValue(config))
configSchema, diags := b.PrepareConfig(configSchema) configSchema, diags := b.PrepareConfig(configSchema)
if len(diags) > 0 { if len(diags) > 0 {
@ -598,13 +602,15 @@ func TestBackendConfig_AssumeRole(t *testing.T) {
testCase := testCase testCase := testCase
t.Run(testCase.Description, func(t *testing.T) { t.Run(testCase.Description, func(t *testing.T) {
ctx := context.Background()
closeSts, _, endpoint := mockdata.GetMockedAwsApiSession("STS", testCase.MockStsEndpoints) closeSts, _, endpoint := mockdata.GetMockedAwsApiSession("STS", testCase.MockStsEndpoints)
defer closeSts() defer closeSts()
testCase.Config["sts_endpoint"] = endpoint testCase.Config["sts_endpoint"] = endpoint
b := New() b := New()
diags := b.Configure(populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(testCase.Config))) diags := b.Configure(populateSchema(t, b.ConfigSchema(ctx), hcl2shim.HCL2ValueFromConfigValue(testCase.Config)))
if diags.HasErrors() { if diags.HasErrors() {
for _, diag := range diags { for _, diag := range diags {
@ -732,7 +738,9 @@ func TestBackendConfig_PrepareConfigValidation(t *testing.T) {
b := New() b := New()
_, valDiags := b.PrepareConfig(populateSchema(t, b.ConfigSchema(), tc.config)) ctx := context.Background()
_, valDiags := b.PrepareConfig(populateSchema(t, b.ConfigSchema(ctx), tc.config))
if tc.expectedErr != "" { if tc.expectedErr != "" {
if valDiags.Err() != nil { if valDiags.Err() != nil {
actualErr := valDiags.Err().Error() actualErr := valDiags.Err().Error()
@ -801,7 +809,9 @@ func TestBackendConfig_PrepareConfigWithEnvVars(t *testing.T) {
os.Setenv(k, v) os.Setenv(k, v)
} }
_, valDiags := b.PrepareConfig(populateSchema(t, b.ConfigSchema(), tc.config)) ctx := context.Background()
_, valDiags := b.PrepareConfig(populateSchema(t, b.ConfigSchema(ctx), tc.config))
if tc.expectedErr != "" { if tc.expectedErr != "" {
if valDiags.Err() != nil { if valDiags.Err() != nil {
actualErr := valDiags.Err().Error() actualErr := valDiags.Err().Error()
@ -904,7 +914,9 @@ func TestBackendSSECustomerKeyConfig(t *testing.T) {
} }
b := New().(*Backend) b := New().(*Backend)
diags := b.Configure(populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config))) ctx := context.Background()
diags := b.Configure(populateSchema(t, b.ConfigSchema(ctx), hcl2shim.HCL2ValueFromConfigValue(config)))
if testCase.expectedErr != "" { if testCase.expectedErr != "" {
if diags.Err() != nil { if diags.Err() != nil {
@ -971,7 +983,9 @@ func TestBackendSSECustomerKeyEnvVar(t *testing.T) {
}) })
b := New().(*Backend) b := New().(*Backend)
diags := b.Configure(populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config))) ctx := context.Background()
diags := b.Configure(populateSchema(t, b.ConfigSchema(ctx), hcl2shim.HCL2ValueFromConfigValue(config)))
if testCase.expectedErr != "" { if testCase.expectedErr != "" {
if diags.Err() != nil { if diags.Err() != nil {

View File

@ -106,7 +106,7 @@ func New(services *disco.Disco) *Remote {
} }
// ConfigSchema implements backend.Enhanced. // ConfigSchema implements backend.Enhanced.
func (b *Remote) ConfigSchema() *configschema.Block { func (b *Remote) ConfigSchema(context.Context) *configschema.Block {
return &configschema.Block{ return &configschema.Block{
Attributes: map[string]*configschema.Attribute{ Attributes: map[string]*configschema.Attribute{
"hostname": { "hostname": {

View File

@ -4,6 +4,7 @@
package backend package backend
import ( import (
"context"
"reflect" "reflect"
"sort" "sort"
"testing" "testing"
@ -36,7 +37,9 @@ func TestBackendConfig(t *testing.T, b Backend, c hcl.Body) Backend {
c = hcl.EmptyBody() c = hcl.EmptyBody()
} }
schema := b.ConfigSchema() ctx := context.Background()
schema := b.ConfigSchema(ctx)
spec := schema.DecoderSpec() spec := schema.DecoderSpec()
obj, decDiags := hcldec.Decode(c, spec, nil) obj, decDiags := hcldec.Decode(c, spec, nil)
diags = diags.Append(decDiags) diags = diags.Append(decDiags)

View File

@ -4,6 +4,7 @@
package tf package tf
import ( import (
"context"
"fmt" "fmt"
"log" "log"
@ -222,7 +223,9 @@ func getBackend(cfg cty.Value) (backend.Backend, cty.Value, tfdiags.Diagnostics)
config = cty.ObjectVal(config.AsValueMap()) config = cty.ObjectVal(config.AsValueMap())
} }
schema := b.ConfigSchema() ctx := context.TODO()
schema := b.ConfigSchema(ctx)
// Try to coerce the provided value into the desired configuration type. // Try to coerce the provided value into the desired configuration type.
configVal, err := schema.CoerceValue(config) configVal, err := schema.CoerceValue(config)
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@
package tf package tf
import ( import (
"context"
"fmt" "fmt"
"log" "log"
"testing" "testing"
@ -344,7 +345,7 @@ func TestState_validation(t *testing.T) {
type backendFailsConfigure struct{} type backendFailsConfigure struct{}
func (b backendFailsConfigure) ConfigSchema() *configschema.Block { func (b backendFailsConfigure) ConfigSchema(context.Context) *configschema.Block {
log.Printf("[TRACE] backendFailsConfigure.ConfigSchema") log.Printf("[TRACE] backendFailsConfigure.ConfigSchema")
return &configschema.Block{} // intentionally empty configuration schema return &configschema.Block{} // intentionally empty configuration schema
} }

View File

@ -118,7 +118,7 @@ func New(services *disco.Disco) *Cloud {
} }
// ConfigSchema implements backend.Enhanced. // ConfigSchema implements backend.Enhanced.
func (b *Cloud) ConfigSchema() *configschema.Block { func (b *Cloud) ConfigSchema(context.Context) *configschema.Block {
return &configschema.Block{ return &configschema.Block{
Attributes: map[string]*configschema.Attribute{ Attributes: map[string]*configschema.Attribute{
"hostname": { "hostname": {

View File

@ -773,7 +773,10 @@ func testBackendState(t *testing.T, s *states.State, c int) (*legacy.State, *htt
Config: configs.SynthBody("<testBackendState>", map[string]cty.Value{}), Config: configs.SynthBody("<testBackendState>", map[string]cty.Value{}),
} }
b := backendInit.Backend("http")() b := backendInit.Backend("http")()
configSchema := b.ConfigSchema()
ctx := context.Background()
configSchema := b.ConfigSchema(ctx)
hash := backendConfig.Hash(configSchema) hash := backendConfig.Hash(configSchema)
state := legacy.NewState() state := legacy.NewState()

View File

@ -464,7 +464,7 @@ func (c *InitCommand) initBackend(ctx context.Context, root *configs.Module, ext
} }
b := bf() b := bf()
backendSchema := b.ConfigSchema() backendSchema := b.ConfigSchema(ctx)
backendConfig = root.Backend backendConfig = root.Backend
var overrideDiags tfdiags.Diagnostics var overrideDiags tfdiags.Diagnostics

View File

@ -313,7 +313,9 @@ func (m *Meta) BackendForLocalPlan(settings plans.Backend) (backend.Enhanced, tf
b := f() b := f()
log.Printf("[TRACE] Meta.BackendForLocalPlan: instantiated backend of type %T", b) log.Printf("[TRACE] Meta.BackendForLocalPlan: instantiated backend of type %T", b)
schema := b.ConfigSchema() ctx := context.TODO()
schema := b.ConfigSchema(ctx)
configVal, err := settings.Config.Decode(schema.ImpliedType()) configVal, err := settings.Config.Decode(schema.ImpliedType())
if err != nil { if err != nil {
diags = diags.Append(fmt.Errorf("saved backend configuration is invalid: %w", err)) diags = diags.Append(fmt.Errorf("saved backend configuration is invalid: %w", err))
@ -404,7 +406,9 @@ func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
// to modify fields of the operation such as Sequence to specify what will // to modify fields of the operation such as Sequence to specify what will
// be called. // be called.
func (m *Meta) Operation(b backend.Backend, vt arguments.ViewType) *backend.Operation { func (m *Meta) Operation(b backend.Backend, vt arguments.ViewType) *backend.Operation {
schema := b.ConfigSchema() ctx := context.TODO()
schema := b.ConfigSchema(ctx)
workspace, err := m.Workspace() workspace, err := m.Workspace()
if err != nil { if err != nil {
// An invalid workspace error would have been raised when creating the // An invalid workspace error would have been raised when creating the
@ -493,7 +497,9 @@ func (m *Meta) backendConfig(opts *BackendOpts) (*configs.Backend, int, tfdiags.
} }
b := bf() b := bf()
configSchema := b.ConfigSchema() ctx := context.TODO()
configSchema := b.ConfigSchema(ctx)
configBody := c.Config configBody := c.Config
configHash := c.Hash(configSchema) configHash := c.Hash(configSchema)
@ -812,7 +818,7 @@ func (m *Meta) backendFromState(ctx context.Context) (backend.Backend, tfdiags.D
// The configuration saved in the working directory state file is used // The configuration saved in the working directory state file is used
// in this case, since it will contain any additional values that // in this case, since it will contain any additional values that
// were provided via -backend-config arguments on tofu init. // were provided via -backend-config arguments on tofu init.
schema := b.ConfigSchema() schema := b.ConfigSchema(ctx)
configVal, err := s.Backend.Config(schema) configVal, err := s.Backend.Config(schema)
if err != nil { if err != nil {
diags = diags.Append(tfdiags.Sourceless( diags = diags.Append(tfdiags.Sourceless(
@ -1057,7 +1063,9 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local
defer stateLocker.Unlock() defer stateLocker.Unlock()
} }
configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType()) ctx := context.TODO()
configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema(ctx).ImpliedType())
if err != nil { if err != nil {
diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %w", err)) diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %w", err))
return nil, diags return nil, diags
@ -1202,7 +1210,9 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista
} }
} }
configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType()) ctx := context.TODO()
configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema(ctx).ImpliedType())
if err != nil { if err != nil {
diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %w", err)) diags = diags.Append(fmt.Errorf("Can't serialize backend configuration as JSON: %w", err))
return nil, diags return nil, diags
@ -1266,10 +1276,12 @@ func (m *Meta) savedBackend(sMgr *clistate.LocalState) (backend.Backend, tfdiags
} }
b := f() b := f()
ctx := context.TODO()
// The configuration saved in the working directory state file is used // The configuration saved in the working directory state file is used
// in this case, since it will contain any additional values that // in this case, since it will contain any additional values that
// were provided via -backend-config arguments on tofu init. // were provided via -backend-config arguments on tofu init.
schema := b.ConfigSchema() schema := b.ConfigSchema(ctx)
configVal, err := s.Backend.Config(schema) configVal, err := s.Backend.Config(schema)
if err != nil { if err != nil {
diags = diags.Append(tfdiags.Sourceless( diags = diags.Append(tfdiags.Sourceless(
@ -1354,7 +1366,9 @@ func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *legacy.Backend
} }
b := f() b := f()
schema := b.ConfigSchema() ctx := context.TODO()
schema := b.ConfigSchema(ctx)
decSpec := schema.NoneRequired().DecoderSpec() decSpec := schema.NoneRequired().DecoderSpec()
givenVal, diags := hcldec.Decode(c.Config, decSpec, nil) givenVal, diags := hcldec.Decode(c.Config, decSpec, nil)
if diags.HasErrors() { if diags.HasErrors() {
@ -1391,7 +1405,9 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V
} }
b := f() b := f()
schema := b.ConfigSchema() ctx := context.TODO()
schema := b.ConfigSchema(ctx)
decSpec := schema.NoneRequired().DecoderSpec() decSpec := schema.NoneRequired().DecoderSpec()
configVal, hclDiags := hcldec.Decode(c.Config, decSpec, nil) configVal, hclDiags := hcldec.Decode(c.Config, decSpec, nil)
diags = diags.Append(hclDiags) diags = diags.Append(hclDiags)

View File

@ -478,7 +478,7 @@ func TestPlan_outBackend(t *testing.T) {
} }
{ {
httpBackend := backendinit.Backend("http")() httpBackend := backendinit.Backend("http")()
schema := httpBackend.ConfigSchema() schema := httpBackend.ConfigSchema(context.Background())
got, err := plan.Backend.Config.Decode(schema.ImpliedType()) got, err := plan.Backend.Config.Decode(schema.ImpliedType())
if err != nil { if err != nil {
t.Fatalf("failed to decode backend config in plan: %s", err) t.Fatalf("failed to decode backend config in plan: %s", err)

View File

@ -48,7 +48,7 @@ func FromContextBackendConfig(ctx context.Context) *ResourceData {
return ctx.Value(backendConfigKey).(*ResourceData) return ctx.Value(backendConfigKey).(*ResourceData)
} }
func (b *Backend) ConfigSchema() *configschema.Block { func (b *Backend) ConfigSchema(context.Context) *configschema.Block {
// This is an alias of CoreConfigSchema just to implement the // This is an alias of CoreConfigSchema just to implement the
// backend.Backend interface. // backend.Backend interface.
return b.CoreConfigSchema() return b.CoreConfigSchema()