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) {
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
}})

View File

@ -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 {

View File

@ -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
}

View File

@ -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")
}