diff --git a/daemon/control.go b/daemon/control.go index 7e84b6f..edd1624 100644 --- a/daemon/control.go +++ b/daemon/control.go @@ -120,7 +120,13 @@ func (j *controlJob) Run(ctx context.Context) { jsonResponder{log, func() (interface{}, error) { jobs := j.jobs.status() globalZFS := zfscmd.GetReport() - s := Status{Jobs: jobs, Global: GlobalStatus{ZFSCmds: globalZFS}} + envconstReport := envconst.GetReport() + s := Status{ + Jobs: jobs, + Global: GlobalStatus{ + ZFSCmds: globalZFS, + Envconst: envconstReport, + }} return s, nil }}) diff --git a/daemon/daemon.go b/daemon/daemon.go index 824272d..4c354af 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -14,6 +14,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/zrepl/zrepl/daemon/logging/trace" "github.com/zrepl/zrepl/endpoint" + "github.com/zrepl/zrepl/util/envconst" "github.com/zrepl/zrepl/config" "github.com/zrepl/zrepl/daemon/job" @@ -150,7 +151,8 @@ type Status struct { } type GlobalStatus struct { - ZFSCmds *zfscmd.Report + ZFSCmds *zfscmd.Report + Envconst *envconst.Report } func (s *jobs) status() map[string]*job.Status { diff --git a/util/envconst/envconst.go b/util/envconst/envconst.go index 8c965fb..1dfc6eb 100644 --- a/util/envconst/envconst.go +++ b/util/envconst/envconst.go @@ -12,84 +12,100 @@ import ( var cache sync.Map -func Duration(varname string, def time.Duration) time.Duration { +func Reset() { + cache.Range(func(key, _ interface{}) bool { + cache.Delete(key) + return true + }) +} + +func Duration(varname string, def time.Duration) (d time.Duration) { + var err error if v, ok := cache.Load(varname); ok { return v.(time.Duration) } e := os.Getenv(varname) if e == "" { - return def - } - d, err := time.ParseDuration(e) - if err != nil { - panic(err) + d = def + } else { + d, err = time.ParseDuration(e) + if err != nil { + panic(err) + } } cache.Store(varname, d) return d } -func Int(varname string, def int) int { +func Int(varname string, def int) (d int) { if v, ok := cache.Load(varname); ok { return v.(int) } e := os.Getenv(varname) if e == "" { - return def + d = def + } else { + d64, err := strconv.ParseInt(e, 10, strconv.IntSize) + if err != nil { + panic(err) + } + d = int(d64) } - d64, err := strconv.ParseInt(e, 10, strconv.IntSize) - if err != nil { - panic(err) - } - d := int(d64) cache.Store(varname, d) return d } -func Int64(varname string, def int64) int64 { +func Int64(varname string, def int64) (d int64) { + var err error if v, ok := cache.Load(varname); ok { return v.(int64) } e := os.Getenv(varname) if e == "" { - return def - } - d, err := strconv.ParseInt(e, 10, 64) - if err != nil { - panic(err) + d = def + } else { + d, err = strconv.ParseInt(e, 10, 64) + if err != nil { + panic(err) + } } cache.Store(varname, d) return d } -func Bool(varname string, def bool) bool { +func Bool(varname string, def bool) (d bool) { + var err error if v, ok := cache.Load(varname); ok { return v.(bool) } e := os.Getenv(varname) if e == "" { - return def - } - d, err := strconv.ParseBool(e) - if err != nil { - panic(err) + d = def + } else { + d, err = strconv.ParseBool(e) + if err != nil { + panic(err) + } } cache.Store(varname, d) return d } -func String(varname string, def string) string { +func String(varname string, def string) (d string) { if v, ok := cache.Load(varname); ok { return v.(string) } e := os.Getenv(varname) if e == "" { - return def + d = def + } else { + d = e } - cache.Store(varname, e) - return e + cache.Store(varname, d) + return d } -func Var(varname string, def flag.Value) interface{} { +func Var(varname string, def flag.Value) (d interface{}) { // use def's type to instantiate a new object of that same type // and call flag.Value.Set() on it @@ -100,19 +116,43 @@ func Var(varname string, def flag.Value) interface{} { defElemType := defType.Elem() if v, ok := cache.Load(varname); ok { - return v.(string) + return v } + e := os.Getenv(varname) if e == "" { - return def + d = def + } else { + newInstance := reflect.New(defElemType) + if err := newInstance.Interface().(flag.Value).Set(e); err != nil { + panic(err) + } + d = newInstance.Interface() } - newInstance := reflect.New(defElemType) - if err := newInstance.Interface().(flag.Value).Set(e); err != nil { - panic(err) - } - - res := newInstance.Interface() - cache.Store(varname, res) - return res + cache.Store(varname, d) + return d +} + +type Report struct { + Entries []EntryReport +} + +type EntryReport struct { + Var string + Value string + ValueGoType string +} + +func GetReport() *Report { + var r Report + cache.Range(func(key, value interface{}) bool { + r.Entries = append(r.Entries, EntryReport{ + Var: key.(string), + Value: fmt.Sprintf("%v", value), + ValueGoType: fmt.Sprintf("%T", value), + }) + return true + }) + return &r } diff --git a/util/envconst/envconst_test.go b/util/envconst/envconst_test.go index bc4af67..52c7867 100644 --- a/util/envconst/envconst_test.go +++ b/util/envconst/envconst_test.go @@ -32,19 +32,27 @@ func (m *ExampleVarType) Set(s string) error { const EnvVarName = "ZREPL_ENVCONST_UNIT_TEST_VAR" -func TestVar(t *testing.T) { +func TestVarDefaultValue(t *testing.T) { + envconst.Reset() _, set := os.LookupEnv(EnvVarName) require.False(t, set) defer os.Unsetenv(EnvVarName) val := envconst.Var(EnvVarName, &Var1) if &Var1 != val { - t.Errorf("default value shut be same address") + t.Errorf("default value should be same address") } +} + +func TestVarOverriddenValue(t *testing.T) { + envconst.Reset() + _, set := os.LookupEnv(EnvVarName) + require.False(t, set) + defer os.Unsetenv(EnvVarName) err := os.Setenv(EnvVarName, "var2") require.NoError(t, err) - val = envconst.Var(EnvVarName, &Var1) + val := envconst.Var(EnvVarName, &Var1) require.Equal(t, &Var2, val, "only structural identity is required for non-default vars") }