Skip to content

Commit

Permalink
fix: require an ftl-project.toml file
Browse files Browse the repository at this point in the history
This is a global requirement across all commands, not scoped
specifically to `ftl serve` or `ftl dev`, because eg. `ftl secret`/`ftl
config`, etc. etc. all require this config file, and it's just
cleaner/safer to do it in one location.

Fixes #1669
  • Loading branch information
alecthomas committed Jun 13, 2024
1 parent 44163b4 commit 4fc42a6
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 16 deletions.
4 changes: 2 additions & 2 deletions backend/controller/admin/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func configProviderKey(p *ftlv1.ConfigProvider) string {
// ConfigSet sets the configuration at the given ref to the provided value.
func (s *AdminService) ConfigSet(ctx context.Context, req *connect.Request[ftlv1.SetConfigRequest]) (*connect.Response[ftlv1.SetConfigResponse], error) {
pkey := configProviderKey(req.Msg.Provider)
err := s.cm.Set(ctx, pkey, cf.NewRef(*req.Msg.Ref.Module, req.Msg.Ref.Name), string(req.Msg.Value))
err := s.cm.SetJSON(ctx, pkey, cf.NewRef(*req.Msg.Ref.Module, req.Msg.Ref.Name), req.Msg.Value)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -185,7 +185,7 @@ func secretProviderKey(p *ftlv1.SecretProvider) string {
// SecretSet sets the secret at the given ref to the provided value.
func (s *AdminService) SecretSet(ctx context.Context, req *connect.Request[ftlv1.SetSecretRequest]) (*connect.Response[ftlv1.SetSecretResponse], error) {
pkey := secretProviderKey(req.Msg.Provider)
err := s.sm.Set(ctx, pkey, cf.NewRef(*req.Msg.Ref.Module, req.Msg.Ref.Name), string(req.Msg.Value))
err := s.sm.SetJSON(ctx, pkey, cf.NewRef(*req.Msg.Ref.Module, req.Msg.Ref.Name), req.Msg.Value)
if err != nil {
return nil, err
}
Expand Down
14 changes: 9 additions & 5 deletions cmd/ftl/cmd_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ func (s *secretSetCmd) Run(ctx context.Context, scmd *secretCmd, adminClient adm
var err error
var secret []byte
if isatty.IsTerminal(0) {
fmt.Print("Secret: ")
secret, err = term.ReadPassword(0)
fmt.Println()
if err != nil {
Expand All @@ -124,18 +123,23 @@ func (s *secretSetCmd) Run(ctx context.Context, scmd *secretCmd, adminClient adm
}
}

var secretValue []byte
var secretJSON json.RawMessage
if s.JSON {
if err := json.Unmarshal(secret, &secretValue); err != nil {
var jsonValue any
if err := json.Unmarshal(secret, &jsonValue); err != nil {
return fmt.Errorf("secret is not valid JSON: %w", err)
}
secretJSON = secret
} else {
secretValue = secret
secretJSON, err = json.Marshal(string(secret))
if err != nil {
return fmt.Errorf("failed to encode secret as JSON: %w", err)
}
}

req := &ftlv1.SetSecretRequest{
Ref: configRefFromRef(s.Ref),
Value: secretValue,
Value: secretJSON,
}
if provider, ok := scmd.provider().Get(); ok {
req.Provider = &provider
Expand Down
15 changes: 12 additions & 3 deletions cmd/ftl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,23 @@ func main() {
logger := log.Configure(os.Stderr, cli.LogConfig)
ctx = log.ContextWithLogger(ctx, logger)

config, err := projectconfig.Load(ctx, cli.ConfigFlag)
configPath := cli.ConfigFlag
if configPath == "" {
var ok bool
configPath, ok = projectconfig.DefaultConfigPath().Get()
if !ok {
kctx.Fatalf("could not determine default config path, place an ftl-project.toml file in your git root directory, use --config=FILE, or set the FTL_CONFIG envar")
}
}

config, err := projectconfig.Load(ctx, configPath)
if err != nil && !errors.Is(err, os.ErrNotExist) {
kctx.Fatalf(err.Error())
}
kctx.Bind(config)

sr := cf.ProjectConfigResolver[cf.Secrets]{Config: cli.ConfigFlag}
cr := cf.ProjectConfigResolver[cf.Configuration]{Config: cli.ConfigFlag}
sr := cf.ProjectConfigResolver[cf.Secrets]{Config: configPath}
cr := cf.ProjectConfigResolver[cf.Configuration]{Config: configPath}
kctx.BindTo(sr, (*cf.Resolver[cf.Secrets])(nil))
kctx.BindTo(cr, (*cf.Resolver[cf.Configuration])(nil))

Expand Down
28 changes: 22 additions & 6 deletions common/configuration/manager.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package configuration

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -108,18 +109,26 @@ func (m *Manager[R]) availableProviderKeys() []string {
return keys
}

// Set a configuration value.
// Set a configuration value, encoding "value" as JSON before storing it.
func (m *Manager[R]) Set(ctx context.Context, pkey string, ref Ref, value any) error {
data, err := json.Marshal(value)
if err != nil {
return err
}
return m.SetJSON(ctx, pkey, ref, data)
}

// SetJSON sets a configuration value using raw JSON data.
func (m *Manager[R]) SetJSON(ctx context.Context, pkey string, ref Ref, value json.RawMessage) error {
if err := checkJSON(value); err != nil {
return fmt.Errorf("invalid value for %s, must be JSON: %w", m.resolver.Role(), err)
}
provider, ok := m.providers[pkey]
if !ok {
pkeys := strings.Join(m.availableProviderKeys(), ", ")
return fmt.Errorf("no provider for key %q, specify one of: %s", pkey, pkeys)
}
data, err := json.Marshal(value)
if err != nil {
return err
}
key, err := provider.Store(ctx, ref, data)
key, err := provider.Store(ctx, ref, value)
if err != nil {
return err
}
Expand Down Expand Up @@ -173,3 +182,10 @@ func (m *Manager[R]) Unset(ctx context.Context, pkey string, ref Ref) error {
func (m *Manager[R]) List(ctx context.Context) ([]Entry, error) {
return m.resolver.List(ctx)
}

func checkJSON(data []byte) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
var v any
return dec.Decode(&v)
}

0 comments on commit 4fc42a6

Please sign in to comment.