envconst: queryable report of resolved variables + integration inot zrepl status --raw

fixes #299
refs #186
This commit is contained in:
Christian Schwarz 2020-06-07 12:24:16 +02:00
parent b330ccca5d
commit 4b1b7a8561
4 changed files with 101 additions and 45 deletions

View File

@ -120,7 +120,13 @@ func (j *controlJob) Run(ctx context.Context) {
jsonResponder{log, func() (interface{}, error) { jsonResponder{log, func() (interface{}, error) {
jobs := j.jobs.status() jobs := j.jobs.status()
globalZFS := zfscmd.GetReport() 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 return s, nil
}}) }})

View File

@ -14,6 +14,7 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/zrepl/zrepl/daemon/logging/trace" "github.com/zrepl/zrepl/daemon/logging/trace"
"github.com/zrepl/zrepl/endpoint" "github.com/zrepl/zrepl/endpoint"
"github.com/zrepl/zrepl/util/envconst"
"github.com/zrepl/zrepl/config" "github.com/zrepl/zrepl/config"
"github.com/zrepl/zrepl/daemon/job" "github.com/zrepl/zrepl/daemon/job"
@ -150,7 +151,8 @@ type Status struct {
} }
type GlobalStatus struct { type GlobalStatus struct {
ZFSCmds *zfscmd.Report ZFSCmds *zfscmd.Report
Envconst *envconst.Report
} }
func (s *jobs) status() map[string]*job.Status { func (s *jobs) status() map[string]*job.Status {

View File

@ -12,84 +12,100 @@ import (
var cache sync.Map 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 { if v, ok := cache.Load(varname); ok {
return v.(time.Duration) return v.(time.Duration)
} }
e := os.Getenv(varname) e := os.Getenv(varname)
if e == "" { if e == "" {
return def d = def
} } else {
d, err := time.ParseDuration(e) d, err = time.ParseDuration(e)
if err != nil { if err != nil {
panic(err) panic(err)
}
} }
cache.Store(varname, d) cache.Store(varname, d)
return 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 { if v, ok := cache.Load(varname); ok {
return v.(int) return v.(int)
} }
e := os.Getenv(varname) e := os.Getenv(varname)
if e == "" { 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) cache.Store(varname, d)
return 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 { if v, ok := cache.Load(varname); ok {
return v.(int64) return v.(int64)
} }
e := os.Getenv(varname) e := os.Getenv(varname)
if e == "" { if e == "" {
return def d = def
} } else {
d, err := strconv.ParseInt(e, 10, 64) d, err = strconv.ParseInt(e, 10, 64)
if err != nil { if err != nil {
panic(err) panic(err)
}
} }
cache.Store(varname, d) cache.Store(varname, d)
return 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 { if v, ok := cache.Load(varname); ok {
return v.(bool) return v.(bool)
} }
e := os.Getenv(varname) e := os.Getenv(varname)
if e == "" { if e == "" {
return def d = def
} } else {
d, err := strconv.ParseBool(e) d, err = strconv.ParseBool(e)
if err != nil { if err != nil {
panic(err) panic(err)
}
} }
cache.Store(varname, d) cache.Store(varname, d)
return 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 { if v, ok := cache.Load(varname); ok {
return v.(string) return v.(string)
} }
e := os.Getenv(varname) e := os.Getenv(varname)
if e == "" { if e == "" {
return def d = def
} else {
d = e
} }
cache.Store(varname, e) cache.Store(varname, d)
return e 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 // use def's type to instantiate a new object of that same type
// and call flag.Value.Set() on it // and call flag.Value.Set() on it
@ -100,19 +116,43 @@ func Var(varname string, def flag.Value) interface{} {
defElemType := defType.Elem() defElemType := defType.Elem()
if v, ok := cache.Load(varname); ok { if v, ok := cache.Load(varname); ok {
return v.(string) return v
} }
e := os.Getenv(varname) e := os.Getenv(varname)
if e == "" { 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) cache.Store(varname, d)
if err := newInstance.Interface().(flag.Value).Set(e); err != nil { return d
panic(err) }
}
type Report struct {
res := newInstance.Interface() Entries []EntryReport
cache.Store(varname, res) }
return res
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
} }

View File

@ -32,19 +32,27 @@ func (m *ExampleVarType) Set(s string) error {
const EnvVarName = "ZREPL_ENVCONST_UNIT_TEST_VAR" const EnvVarName = "ZREPL_ENVCONST_UNIT_TEST_VAR"
func TestVar(t *testing.T) { func TestVarDefaultValue(t *testing.T) {
envconst.Reset()
_, set := os.LookupEnv(EnvVarName) _, set := os.LookupEnv(EnvVarName)
require.False(t, set) require.False(t, set)
defer os.Unsetenv(EnvVarName) defer os.Unsetenv(EnvVarName)
val := envconst.Var(EnvVarName, &Var1) val := envconst.Var(EnvVarName, &Var1)
if &Var1 != val { 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") err := os.Setenv(EnvVarName, "var2")
require.NoError(t, err) 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") require.Equal(t, &Var2, val, "only structural identity is required for non-default vars")
} }