diff --git a/common/configuration/api.go b/common/configuration/api.go index 992d2e2ca7..e0717547d4 100644 --- a/common/configuration/api.go +++ b/common/configuration/api.go @@ -34,7 +34,6 @@ type Entry struct { // A Ref is a reference to a configuration value. type Ref struct { - // If not present, the Ref is considered to be global. Module optional.Option[string] Name string } diff --git a/examples/go/echo/echo.go b/examples/go/echo/echo.go index 45d3affabc..175eeaca47 100644 --- a/examples/go/echo/echo.go +++ b/examples/go/echo/echo.go @@ -30,5 +30,5 @@ func Echo(ctx context.Context, req EchoRequest) (EchoResponse, error) { return EchoResponse{}, err } - return EchoResponse{Message: fmt.Sprintf("Hello, %s!!! It is %s!", req.Name.Default(defaultName.Get()), tresp.Time)}, nil + return EchoResponse{Message: fmt.Sprintf("Hello, %s!!! It is %s!", req.Name.Default(defaultName.Get(ctx)), tresp.Time)}, nil } diff --git a/go-runtime/ftl/config.go b/go-runtime/ftl/config.go index 6f0593b4a3..70588e502c 100644 --- a/go-runtime/ftl/config.go +++ b/go-runtime/ftl/config.go @@ -3,43 +3,23 @@ package ftl import ( "context" "fmt" - "os" "runtime" "strings" - "sync" - cf "github.com/TBD54566975/ftl/common/configuration" - "github.com/TBD54566975/ftl/internal/log" + "github.com/TBD54566975/ftl/common/configuration" ) -var globalConfigurationManagerOnce sync.Once +// ConfigType is a type that can be used as a configuration value. +type ConfigType interface{ any } -// globalConfigurationManager returns a global configuration manager instance. -func globalConfigurationManager() (manager *cf.Manager[cf.Configuration]) { - globalConfigurationManagerOnce.Do(func() { - var configs []string - if envar, ok := os.LookupEnv("FTL_CONFIG"); ok { - configs = strings.Split(envar, ",") - } - config := cf.DefaultConfigMixin{} - var err error - ctx := log.ContextWithNewDefaultLogger(context.Background()) - manager, err = config.NewConfigurationManager(ctx, cf.ProjectConfigResolver[cf.Configuration]{Config: configs}) - if err != nil { - panic("failed to create global configuration manager: " + err.Error()) - } - }) - return manager -} - -// Config loads a typed configuration value for the current module. -func Config[T any](name string) ConfigValue[T] { +// Config declares a typed configuration key for the current module. +func Config[T ConfigType](name string) ConfigValue[T] { module := callerModule() return ConfigValue[T]{module, name} } // ConfigValue is a typed configuration key for the current module. -type ConfigValue[T any] struct { +type ConfigValue[T ConfigType] struct { module string name string } @@ -52,11 +32,12 @@ func (c ConfigValue[T]) GoString() string { } // Get returns the value of the configuration key from FTL. -func (c ConfigValue[T]) Get() (out T) { - cm := globalConfigurationManager() - ctx := log.ContextWithNewDefaultLogger(context.Background()) - if err := cm.Get(ctx, cf.NewRef(c.module, c.name), &out); err != nil { - panic(fmt.Errorf("failed to get configuration %s.%s: %w", c.module, c.name, err)) +func (c ConfigValue[T]) Get(ctx context.Context) (out T) { + cm := configuration.ConfigFromContext(ctx) + ref := configuration.NewRef(c.module, c.name) + err := cm.Get(ctx, ref, &out) + if err != nil { + panic(fmt.Errorf("failed to get %s: %w", c, err)) } return } diff --git a/go-runtime/ftl/config_test.go b/go-runtime/ftl/config_test.go index 61b0eedad9..4b968804a3 100644 --- a/go-runtime/ftl/config_test.go +++ b/go-runtime/ftl/config_test.go @@ -1,17 +1,27 @@ package ftl import ( + "context" "testing" "github.com/alecthomas/assert/v2" + + "github.com/TBD54566975/ftl/common/configuration" + "github.com/TBD54566975/ftl/common/projectconfig" + "github.com/TBD54566975/ftl/internal/log" ) func TestConfig(t *testing.T) { - t.Setenv("FTL_CONFIG", "testdata/ftl-project.toml") + ctx := log.ContextWithNewDefaultLogger(context.Background()) + cr := configuration.ProjectConfigResolver[configuration.Configuration]{Config: []string{"testdata/ftl-project.toml"}} + assert.Equal(t, []string{"testdata/ftl-project.toml"}, projectconfig.ConfigPaths(cr.Config)) + cm, err := configuration.NewConfigurationManager(ctx, cr) + assert.NoError(t, err) + ctx = configuration.ContextWithConfig(ctx, cm) type C struct { One string Two string } config := Config[C]("test") - assert.Equal(t, C{"one", "two"}, config.Get()) + assert.Equal(t, C{"one", "two"}, config.Get(ctx)) } diff --git a/go-runtime/ftl/secrets.go b/go-runtime/ftl/secrets.go index 5e3c63303a..c094238a60 100644 --- a/go-runtime/ftl/secrets.go +++ b/go-runtime/ftl/secrets.go @@ -3,42 +3,21 @@ package ftl import ( "context" "fmt" - "os" - "strings" - "sync" - cf "github.com/TBD54566975/ftl/common/configuration" - "github.com/TBD54566975/ftl/internal/log" + "github.com/TBD54566975/ftl/common/configuration" ) -var globalSecretsManagerOnce sync.Once - -// globalSecretsManager returns a global secrets manager instance. -func globalSecretsManager() (manager *cf.Manager[cf.Secrets]) { - globalSecretsManagerOnce.Do(func() { - var configs []string - if envar, ok := os.LookupEnv("FTL_CONFIG"); ok { - configs = strings.Split(envar, ",") - } - config := cf.DefaultSecretsMixin{} - var err error - ctx := log.ContextWithNewDefaultLogger(context.Background()) - manager, err = config.NewSecretsManager(ctx, cf.ProjectConfigResolver[cf.Secrets]{Config: configs}) - if err != nil { - panic("failed to create global secrets manager: " + err.Error()) - } - }) - return manager -} +// SecretType is a type that can be used as a secret value. +type SecretType interface{ any } // Secret declares a typed secret for the current module. -func Secret[T any](name string) SecretValue[T] { +func Secret[T SecretType](name string) SecretValue[T] { module := callerModule() return SecretValue[T]{module, name} } // SecretValue is a typed secret for the current module. -type SecretValue[T any] struct { +type SecretValue[T SecretType] struct { module string name string } @@ -51,11 +30,10 @@ func (s SecretValue[T]) GoString() string { } // Get returns the value of the secret from FTL. -func (s SecretValue[T]) Get() (out T) { - sm := globalSecretsManager() - ctx := log.ContextWithNewDefaultLogger(context.Background()) - if err := sm.Get(ctx, cf.NewRef(s.module, s.name), &out); err != nil { - panic(fmt.Errorf("failed to get secrets %s.%s: %w", s.module, s.name, err)) +func (s SecretValue[T]) Get(ctx context.Context) (out T) { + sm := configuration.SecretsFromContext(ctx) + if err := sm.Get(ctx, configuration.NewRef(s.module, s.name), &out); err != nil { + panic(fmt.Errorf("failed to get %s: %w", s, err)) } return } diff --git a/go-runtime/ftl/secrets_test.go b/go-runtime/ftl/secrets_test.go index 2047382fab..47ed08d0e0 100644 --- a/go-runtime/ftl/secrets_test.go +++ b/go-runtime/ftl/secrets_test.go @@ -1,17 +1,25 @@ package ftl import ( + "context" "testing" "github.com/alecthomas/assert/v2" + + "github.com/TBD54566975/ftl/common/configuration" + "github.com/TBD54566975/ftl/internal/log" ) func TestSecret(t *testing.T) { - t.Setenv("FTL_CONFIG", "testdata/ftl-project.toml") + ctx := log.ContextWithNewDefaultLogger(context.Background()) + sr := configuration.ProjectConfigResolver[configuration.Secrets]{Config: []string{"testdata/ftl-project.toml"}} + sm, err := configuration.NewSecretsManager(ctx, sr) + assert.NoError(t, err) + ctx = configuration.ContextWithSecrets(ctx, sm) type C struct { One string Two string } config := Secret[C]("secret") - assert.Equal(t, C{"one", "two"}, config.Get()) + assert.Equal(t, C{"one", "two"}, config.Get(ctx)) }