From b08944ec72d0daeaa551ac35addf4b36676369e4 Mon Sep 17 00:00:00 2001 From: Marko Kungla Date: Thu, 1 Feb 2024 04:16:22 +0200 Subject: [PATCH] fix: handle opts override and validation Signed-off-by: Marko Kungla --- happy_test.go | 15 +++++--- initializer.go | 61 ++++++++++++++----------------- options.go | 13 +++++++ sdk/logging/test-logger.go | 4 ++ sdk/networking/address/address.go | 16 ++++++-- session.go | 3 ++ 6 files changed, 70 insertions(+), 42 deletions(-) diff --git a/happy_test.go b/happy_test.go index 838ec5f..0abc31b 100644 --- a/happy_test.go +++ b/happy_test.go @@ -38,7 +38,7 @@ func TestDefaultSettings(t *testing.T) { testutils.Equal(t, "1", sess.Get("instance.max").String(), "instance.max") testutils.Equal(t, 1, sess.Get("instance.max").Int(), "instance.max") - testutils.Equal(t, "com.github.happysdk.happy-test", sess.Get("app.slug").String(), "app.slug") + testutils.Equal(t, "com.github.happy-sdk.happy-test", sess.Get("app.slug").String(), "app.slug") return nil }) @@ -52,16 +52,18 @@ func TestDefaultConfig(t *testing.T) { app := happy.New(happy.Settings{}) app.WithLogger(log) app.Do(func(sess *happy.Session, args happy.Args) error { - testutils.Equal(t, 13, sess.Config().Len(), "default runtime config should be empty") + testutils.Equal(t, 14, sess.Config().Len(), "invalid default runtime config key cpunt") host, err := os.Hostname() if err != nil { return err } - addr := fmt.Sprintf("happy://%s/com.github.happysdk.happy-test", host) + addr := fmt.Sprintf("happy://%s/com.github.happy-sdk.happy-test", host) testutils.Equal(t, addr, sess.Get("app.address").String(), "app.address") testutils.Equal(t, true, sess.Get("app.devel").Bool(), "app.devel") testutils.Equal(t, true, sess.Get("app.firstuse").Bool(), "app.firstuse") + testutils.Equal(t, "com.github.happy-sdk.happy", sess.Get("app.instance.namespace").String(), "app.instance.namespace") testutils.Equal(t, false, sess.Get("app.main.exec.x").Bool(), "app.main.exec.x") + testutils.Equal(t, "github.com/happy-sdk/happy", sess.Get("app.module").String(), "app.module") testutils.Equal(t, "default-devel", sess.Get("app.profile.name").String(), "app.profile.name") testutils.Equal(t, "v1.0.0-0xDEV", sess.Get("app.version").String(), "app.version") @@ -80,9 +82,10 @@ func TestDefaultConfig(t *testing.T) { } testutils.Equal(t, home, sess.Get("app.fs.path.home").String(), "app.fs.path.home") - testutils.Equal(t, fmt.Sprintf("%s/config/com.github.happysdk.happy-test/profiles/default-devel", tmpdir), sess.Get("app.fs.path.config").String(), "app.fs.path.config") - testutils.Equal(t, fmt.Sprintf("%s/cache/com.github.happysdk.happy-test/profiles/default-devel", tmpdir), sess.Get("app.fs.path.cache").String(), "app.fs.path.cache") - testutils.Equal(t, fmt.Sprintf("%s/config/com.github.happysdk.happy-test/profiles/default-devel/profile.preferences", tmpdir), sess.Get("app.profile.file").String(), "app.profile.file") + testutils.Equal(t, fmt.Sprintf("%s/config/com.github.happy-sdk.happy-test/profiles/default-devel", tmpdir), sess.Get("app.fs.path.config").String(), "app.fs.path.config") + testutils.Equal(t, fmt.Sprintf("%s/cache/com.github.happy-sdk.happy-test/profiles/default-devel", tmpdir), sess.Get("app.fs.path.cache").String(), "app.fs.path.cache") + testutils.Equal(t, fmt.Sprintf("%s/config/com.github.happy-sdk.happy-test/profiles/default-devel/profile.preferences", tmpdir), sess.Get("app.profile.file").String(), "app.profile.file") + return nil }) app.Run() diff --git a/initializer.go b/initializer.go index 4952644..25d66a4 100644 --- a/initializer.go +++ b/initializer.go @@ -200,6 +200,13 @@ func (i *initializer) Initialize(m *Main) error { return err } m.slug = tmpaddr.Instance() + "-test" + + if err := m.sess.opts.set("app.instance.namespace", tmpaddr.InstanceDomianReverse(), true); err != nil { + return err + } + if err := m.sess.opts.set("app.module", tmpaddr.Module(), true); err != nil { + return err + } } else { curr, err := address.Current() if err != nil { @@ -210,6 +217,7 @@ func (i *initializer) Initialize(m *Main) error { if err := settingsb.SetDefault("app.slug", m.slug); err != nil { return err } + } inst, err := instance.New(m.slug) if err != nil { @@ -261,17 +269,20 @@ func (i *initializer) Initialize(m *Main) error { } for _, opt := range i.pendingOpts { + group := "option" if opt.kind&ConfigOption != 0 { group = "config" } else if opt.kind&SettingsOption != 0 { group = "settings" } - m.sess.Log().Warn("option not used", slog.Group(group, + + m.sess.Log().Warn("option not used", + slog.String("group", group), slog.String("key", opt.key), slog.Any("value", opt.value), slog.Bool("readOnly", opt.kind&ReadOnlyOption == ReadOnlyOption), - )) + ) } m.root.desc = m.sess.Get("app.description").String() @@ -283,35 +294,10 @@ func (i *initializer) Initialize(m *Main) error { } func (i *initializer) unsafeInitSettings(m *Main, settingsb *settings.Blueprint) error { - for key := range m.sess.opts.config { - for _, opt := range i.mainOpts { - if opt.key == key { - val, err := vars.NewValue(opt.value) - if err != nil { - i.log(logging.NewQueueRecord(logging.LevelError, fmt.Sprintf("%s: failed to parse value - %s", opt.key, err.Error()), 4)) - continue - } - if err := m.sess.Set(key, val); err != nil { - i.log(logging.NewQueueRecord(logging.LevelError, fmt.Sprintf("%s: failed to set value - %s", opt.key, err.Error()), 4)) - continue - } - break - } else { - // extend - m.sess.opts.config[opt.key] = opt - } - - } - // if !provided { - // if err := a.session.opts.Set(key, cnf.value); err != nil { - // errs = append(errs, err) - // continue - // } - // } - } - for _, opt := range i.mainOpts { - if !m.sess.Has(opt.key) { + if m.sess.Has(opt.key) { + continue + } else { i.pendingOpts = append(i.pendingOpts, opt) } } @@ -647,12 +633,21 @@ func (i *initializer) unsafeConfigureOptions(m *Main) error { // apply options var pendingOpts []OptionArg for _, opt := range i.pendingOpts { - // apply if it is custom glopal option - m.sess.Log().SystemDebug("opt", slog.Any(opt.key, opt.value)) + m.sess.Log().SystemDebug("config", slog.Any(opt.key, opt.value)) if _, ok := m.sess.opts.config[opt.key]; ok { - if err := opt.apply(m.sess.opts); err != nil { + val, err := vars.New(opt.key, opt.value, opt.kind&ReadOnlyOption != 0) + if err != nil { return err } + if opt.validator != nil { + if err := opt.validator(opt.key, val.Value()); err != nil { + return err + } + } + if err := m.sess.opts.set(opt.key, val, true); err != nil { + return err + } + continue } pendingOpts = append(pendingOpts, opt) diff --git a/options.go b/options.go index e3bcd2e..0acd7d2 100644 --- a/options.go +++ b/options.go @@ -65,6 +65,7 @@ func Option(key string, value any) OptionArg { kind: RuntimeOption, } } + func OptionReadonly(key string, value any) OptionArg { return OptionArg{ key: key, @@ -175,6 +176,11 @@ func (opts *Options) set(key string, value any, override bool) error { var cnf *OptionArg if c, ok := opts.config[key]; ok { cnf = &c + if vv, ok := value.(vars.Variable); ok { + if vv.ReadOnly() { + cnf.kind |= ReadOnlyOption + } + } } else if c, ok := opts.config["*"]; ok { cnf = &c } @@ -325,6 +331,13 @@ func getRuntimeConfig() []OptionArg { kind: ConfigOption | ReadOnlyOption, validator: noopvalidator, }, + { + key: "app.instance.namespace", + value: addr.InstanceDomianReverse(), + desc: "application module", + kind: ConfigOption | ReadOnlyOption, + validator: noopvalidator, + }, { key: "app.address", value: "", diff --git a/sdk/logging/test-logger.go b/sdk/logging/test-logger.go index 5852c68..208f9a9 100644 --- a/sdk/logging/test-logger.go +++ b/sdk/logging/test-logger.go @@ -97,6 +97,10 @@ func (l *TestLogger) Println(msg string, attrs ...slog.Attr) { l.log.Println(msg, attrs...) } +func (l *TestLogger) Printf(format string, v ...any) { + l.log.Printf(format, v...) +} + func (l *TestLogger) HTTP(method, path string, status int, attrs ...slog.Attr) { l.log.HTTP(method, path, status, attrs...) } diff --git a/sdk/networking/address/address.go b/sdk/networking/address/address.go index a7766dc..7ee51ad 100644 --- a/sdk/networking/address/address.go +++ b/sdk/networking/address/address.go @@ -55,7 +55,8 @@ type Address struct { // Instance is the instance component of the happy address, which defines the service mesh the address belongs to. instance string - module string + module string + mrevese string } // String reassembles the Address into a valid URL string. @@ -84,6 +85,10 @@ func (a *Address) Instance() string { return a.instance } +func (a *Address) InstanceDomianReverse() string { + return a.mrevese +} + func (a *Address) Module() string { return a.module } @@ -144,13 +149,15 @@ func FromModule(host, modulepath string) (*Address, error) { if rmdomain && i == 0 { continue } - rev = append(rev, ensure(sl[i])) + rev = append(rev, (sl[i])) } - addr, err := Parse("happy://" + host + "/" + strings.Join(rev, ".")) + mrevese := strings.Join(rev, ".") + addr, err := Parse("happy://" + host + "/" + mrevese) if err != nil { return nil, err } addr.module = modulepath + addr.mrevese = mrevese return addr, nil } @@ -258,6 +265,9 @@ func Parse(rawAddr string) (*Address, error) { } func ensure(in string) string { + if in == "-" { + return in + } var b bytes.Buffer for _, c := range in { isAlnum := unicode.Is(alnum, c) diff --git a/session.go b/session.go index e2ec730..a13b38b 100644 --- a/session.go +++ b/session.go @@ -238,6 +238,9 @@ func (s *Session) Get(key string) vars.Variable { } func (s *Session) Set(key string, val any) error { + if s.Profile().Has(key) { + return fmt.Errorf("setting profile options is not allowed to be set as option, call Profile.Set instead, attempt to set %s = %v", key, val) + } if !s.opts.Accepts(key) { s.Log().Warn("setting non existing runtime options", slog.String("key", key)) return fmt.Errorf("setting non existing runtime options: %s", key)