Update config release levels and add expertise levels

This commit is contained in:
Daniel 2019-10-04 23:49:57 +02:00
parent adc359c15b
commit 807095f9bc
8 changed files with 132 additions and 67 deletions

2
config/doc.go Normal file
View file

@ -0,0 +1,2 @@
// Package config provides a versatile configuration management system.
package config

72
config/expertise.go Normal file
View file

@ -0,0 +1,72 @@
// Package config ... (linter fix)
//nolint:dupl
package config
import (
"fmt"
"sync/atomic"
)
// Expertise Level constants
const (
ExpertiseLevelUser uint8 = 0
ExpertiseLevelExpert uint8 = 1
ExpertiseLevelDeveloper uint8 = 2
ExpertiseLevelNameUser = "user"
ExpertiseLevelNameExpert = "expert"
ExpertiseLevelNameDeveloper = "developer"
expertiseLevelKey = "core/expertiseLevel"
)
var (
expertiseLevel *int32
)
func init() {
var expertiseLevelVal int32
expertiseLevel = &expertiseLevelVal
registerExpertiseLevelOption()
}
func registerExpertiseLevelOption() {
err := Register(&Option{
Name: "Expertise Level",
Key: expertiseLevelKey,
Description: "The Expertise Level controls the perceived complexity. Higher settings will show you more complex settings and information. This might also affect various other things relying on this setting. Modified settings in higher expertise levels stay in effect when switching back. (Unlike the Release Level)",
OptType: OptTypeString,
ExpertiseLevel: ExpertiseLevelUser,
ReleaseLevel: ExpertiseLevelUser,
RequiresRestart: false,
DefaultValue: ExpertiseLevelNameUser,
ExternalOptType: "string list",
ValidationRegex: fmt.Sprintf("^(%s|%s|%s)$", ExpertiseLevelNameUser, ExpertiseLevelNameExpert, ExpertiseLevelNameDeveloper),
})
if err != nil {
panic(err)
}
}
func updateExpertiseLevel() {
new := findStringValue(expertiseLevelKey, "")
switch new {
case ExpertiseLevelNameUser:
atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelUser))
case ExpertiseLevelNameExpert:
atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelExpert))
case ExpertiseLevelNameDeveloper:
atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelDeveloper))
default:
atomic.StoreInt32(expertiseLevel, int32(ExpertiseLevelUser))
}
}
// GetExpertiseLevel returns the current active expertise level.
func GetExpertiseLevel() uint8 {
return uint8(atomic.LoadInt32(expertiseLevel))
}

View file

@ -81,21 +81,7 @@ func findValue(key string) interface{} {
option.Lock() option.Lock()
defer option.Unlock() defer option.Unlock()
// check if option is active if option.ReleaseLevel <= getReleaseLevel() && option.activeValue != nil {
optionActive := true
switch getReleaseLevel() {
case ReleaseLevelStable:
// In stable, only stable is active
optionActive = option.ReleaseLevel == ReleaseLevelStable
case ReleaseLevelBeta:
// In beta, only stable and beta are active
optionActive = option.ReleaseLevel == ReleaseLevelStable || option.ReleaseLevel == ReleaseLevelBeta
case ReleaseLevelExperimental:
// In experimental, everything is active
optionActive = true
}
if optionActive && option.activeValue != nil {
return option.activeValue return option.activeValue
} }

View file

@ -160,21 +160,21 @@ func TestReleaseLevel(t *testing.T) {
// test option level stable // test option level stable
subsystemOption.ReleaseLevel = ReleaseLevelStable subsystemOption.ReleaseLevel = ReleaseLevelStable
err = SetConfigOption(releaseLevelKey, ReleaseLevelStable) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !testSubsystem() { if !testSubsystem() {
t.Error("should be active") t.Error("should be active")
} }
err = SetConfigOption(releaseLevelKey, ReleaseLevelBeta) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !testSubsystem() { if !testSubsystem() {
t.Error("should be active") t.Error("should be active")
} }
err = SetConfigOption(releaseLevelKey, ReleaseLevelExperimental) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -184,21 +184,21 @@ func TestReleaseLevel(t *testing.T) {
// test option level beta // test option level beta
subsystemOption.ReleaseLevel = ReleaseLevelBeta subsystemOption.ReleaseLevel = ReleaseLevelBeta
err = SetConfigOption(releaseLevelKey, ReleaseLevelStable) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if testSubsystem() { if testSubsystem() {
t.Errorf("should be inactive: opt=%s system=%s", subsystemOption.ReleaseLevel, releaseLevel) t.Errorf("should be inactive: opt=%d system=%d", subsystemOption.ReleaseLevel, getReleaseLevel())
} }
err = SetConfigOption(releaseLevelKey, ReleaseLevelBeta) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !testSubsystem() { if !testSubsystem() {
t.Error("should be active") t.Error("should be active")
} }
err = SetConfigOption(releaseLevelKey, ReleaseLevelExperimental) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -208,21 +208,21 @@ func TestReleaseLevel(t *testing.T) {
// test option level experimental // test option level experimental
subsystemOption.ReleaseLevel = ReleaseLevelExperimental subsystemOption.ReleaseLevel = ReleaseLevelExperimental
err = SetConfigOption(releaseLevelKey, ReleaseLevelStable) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameStable)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if testSubsystem() { if testSubsystem() {
t.Error("should be inactive") t.Error("should be inactive")
} }
err = SetConfigOption(releaseLevelKey, ReleaseLevelBeta) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameBeta)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if testSubsystem() { if testSubsystem() {
t.Error("should be inactive") t.Error("should be inactive")
} }
err = SetConfigOption(releaseLevelKey, ReleaseLevelExperimental) err = SetConfigOption(releaseLevelKey, ReleaseLevelNameExperimental)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -17,14 +17,6 @@ const (
OptTypeStringArray uint8 = 2 OptTypeStringArray uint8 = 2
OptTypeInt uint8 = 3 OptTypeInt uint8 = 3
OptTypeBool uint8 = 4 OptTypeBool uint8 = 4
ExpertiseLevelUser uint8 = 1
ExpertiseLevelExpert uint8 = 2
ExpertiseLevelDeveloper uint8 = 3
ReleaseLevelStable = "stable"
ReleaseLevelBeta = "beta"
ReleaseLevelExperimental = "experimental"
) )
func getTypeName(t uint8) string { func getTypeName(t uint8) string {
@ -50,9 +42,9 @@ type Option struct {
Key string // in path format: category/sub/key Key string // in path format: category/sub/key
Description string Description string
ReleaseLevel string
ExpertiseLevel uint8
OptType uint8 OptType uint8
ExpertiseLevel uint8
ReleaseLevel uint8
RequiresRestart bool RequiresRestart bool
DefaultValue interface{} DefaultValue interface{}

View file

@ -1,7 +1,6 @@
package config package config
import ( import (
"errors"
"fmt" "fmt"
"regexp" "regexp"
"sync" "sync"
@ -10,21 +9,21 @@ import (
var ( var (
optionsLock sync.RWMutex optionsLock sync.RWMutex
options = make(map[string]*Option) options = make(map[string]*Option)
// ErrIncompleteCall is return when RegisterOption is called with empty mandatory values.
ErrIncompleteCall = errors.New("could not register config option: all fields, except for the validationRegex are mandatory")
) )
// Register registers a new configuration option. // Register registers a new configuration option.
func Register(option *Option) error { func Register(option *Option) error {
if option.Name == "" {
if option.Name == "" || return fmt.Errorf("failed to register option: please set option.Name")
option.Key == "" || }
option.Description == "" || if option.Key == "" {
option.OptType == 0 || return fmt.Errorf("failed to register option: please set option.Key")
option.ExpertiseLevel == 0 || }
option.ReleaseLevel == "" { if option.Description == "" {
return ErrIncompleteCall return fmt.Errorf("failed to register option: please set option.Description")
}
if option.OptType == 0 {
return fmt.Errorf("failed to register option: please set option.OptType")
} }
if option.ValidationRegex != "" { if option.ValidationRegex != "" {
@ -37,7 +36,6 @@ func Register(option *Option) error {
optionsLock.Lock() optionsLock.Lock()
defer optionsLock.Unlock() defer optionsLock.Unlock()
options[option.Key] = option options[option.Key] = option
return nil return nil

View file

@ -1,38 +1,51 @@
// Package config ... (linter fix)
//nolint:dupl
package config package config
import ( import (
"fmt" "fmt"
"sync" "sync/atomic"
) )
// Release Level constants
const ( const (
releaseLevelKey = "core/release_level" ReleaseLevelStable uint8 = 0
ReleaseLevelBeta uint8 = 1
ReleaseLevelExperimental uint8 = 2
ReleaseLevelNameStable = "stable"
ReleaseLevelNameBeta = "beta"
ReleaseLevelNameExperimental = "experimental"
releaseLevelKey = "core/releaseLevel"
) )
var ( var (
releaseLevel = ReleaseLevelStable releaseLevel *int32
releaseLevelLock sync.Mutex
) )
func init() { func init() {
var releaseLevelVal int32
releaseLevel = &releaseLevelVal
registerReleaseLevelOption() registerReleaseLevelOption()
} }
func registerReleaseLevelOption() { func registerReleaseLevelOption() {
err := Register(&Option{ err := Register(&Option{
Name: "Release Selection", Name: "Release Level",
Key: releaseLevelKey, Key: releaseLevelKey,
Description: "Select maturity level of features that should be available", Description: "The Release Level changes which features are available to you. Some beta or experimental features are also available in the stable release channel. Unavailable settings are set to the default value.",
OptType: OptTypeString, OptType: OptTypeString,
ExpertiseLevel: ExpertiseLevelExpert, ExpertiseLevel: ExpertiseLevelExpert,
ReleaseLevel: ReleaseLevelStable, ReleaseLevel: ReleaseLevelStable,
RequiresRestart: false, RequiresRestart: false,
DefaultValue: ReleaseLevelStable, DefaultValue: ReleaseLevelNameStable,
ExternalOptType: "string list", ExternalOptType: "string list",
ValidationRegex: fmt.Sprintf("^(%s|%s|%s)$", ReleaseLevelStable, ReleaseLevelBeta, ReleaseLevelExperimental), ValidationRegex: fmt.Sprintf("^(%s|%s|%s)$", ReleaseLevelNameStable, ReleaseLevelNameBeta, ReleaseLevelNameExperimental),
}) })
if err != nil { if err != nil {
panic(err) panic(err)
@ -41,17 +54,18 @@ func registerReleaseLevelOption() {
func updateReleaseLevel() { func updateReleaseLevel() {
new := findStringValue(releaseLevelKey, "") new := findStringValue(releaseLevelKey, "")
releaseLevelLock.Lock() switch new {
if new == "" { case ReleaseLevelNameStable:
releaseLevel = ReleaseLevelStable atomic.StoreInt32(releaseLevel, int32(ReleaseLevelStable))
} else { case ReleaseLevelNameBeta:
releaseLevel = new atomic.StoreInt32(releaseLevel, int32(ReleaseLevelBeta))
case ReleaseLevelNameExperimental:
atomic.StoreInt32(releaseLevel, int32(ReleaseLevelExperimental))
default:
atomic.StoreInt32(releaseLevel, int32(ReleaseLevelStable))
} }
releaseLevelLock.Unlock()
} }
func getReleaseLevel() string { func getReleaseLevel() uint8 {
releaseLevelLock.Lock() return uint8(atomic.LoadInt32(releaseLevel))
defer releaseLevelLock.Unlock()
return releaseLevel
} }

View file

@ -36,8 +36,9 @@ func Changed() <-chan struct{} {
} }
func signalChanges() { func signalChanges() {
// refetch and save release level // refetch and save release level and expertise level
updateReleaseLevel() updateReleaseLevel()
updateExpertiseLevel()
// reset validity flag // reset validity flag
validityFlagLock.Lock() validityFlagLock.Lock()