mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Avoid setting default truthy values from flags that are not set.
When the value for a configuration option in the file is `false`, and the default value for a flag is `true`, we should not take the value from the later as final value for the option, because the user explicitly set `false`. This change overrides the default value in the flagSet with the value in the configuration file so we get the correct result when we merge the two configurations together. Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
2e6c841b82
commit
31cb96dcfa
4 changed files with 131 additions and 11 deletions
|
@ -154,14 +154,20 @@ func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReloadConfiguration reads the configuration in the host and reloads the daemon and server.
|
// ReloadConfiguration reads the configuration in the host and reloads the daemon and server.
|
||||||
func ReloadConfiguration(configFile string, flags *flag.FlagSet, reload func(*Config)) {
|
func ReloadConfiguration(configFile string, flags *flag.FlagSet, reload func(*Config)) error {
|
||||||
logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile)
|
logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile)
|
||||||
newConfig, err := getConflictFreeConfiguration(configFile, flags)
|
newConfig, err := getConflictFreeConfiguration(configFile, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
return err
|
||||||
} else {
|
|
||||||
reload(newConfig)
|
|
||||||
}
|
}
|
||||||
|
reload(newConfig)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// boolValue is an interface that boolean value flags implement
|
||||||
|
// to tell the command line how to make -name equivalent to -name=true.
|
||||||
|
type boolValue interface {
|
||||||
|
IsBoolFlag() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeDaemonConfigurations reads a configuration file,
|
// MergeDaemonConfigurations reads a configuration file,
|
||||||
|
@ -206,6 +212,36 @@ func getConflictFreeConfiguration(configFile string, flags *flag.FlagSet) (*Conf
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override flag values to make sure the values set in the config file with nullable values, like `false`,
|
||||||
|
// are not overriden by default truthy values from the flags that were not explicitly set.
|
||||||
|
// See https://github.com/docker/docker/issues/20289 for an example.
|
||||||
|
//
|
||||||
|
// TODO: Rewrite configuration logic to avoid same issue with other nullable values, like numbers.
|
||||||
|
namedOptions := make(map[string]interface{})
|
||||||
|
for key, value := range configSet {
|
||||||
|
f := flags.Lookup("-" + key)
|
||||||
|
if f == nil { // ignore named flags that don't match
|
||||||
|
namedOptions[key] = value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := f.Value.(boolValue); ok {
|
||||||
|
f.Value.Set(fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(namedOptions) > 0 {
|
||||||
|
// set also default for mergeVal flags that are boolValue at the same time.
|
||||||
|
flags.VisitAll(func(f *flag.Flag) {
|
||||||
|
if opt, named := f.Value.(opts.NamedOption); named {
|
||||||
|
v, set := namedOptions[opt.Name()]
|
||||||
|
_, boolean := f.Value.(boolValue)
|
||||||
|
if set && boolean {
|
||||||
|
f.Value.Set(fmt.Sprintf("%v", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
config.valuesSet = configSet
|
config.valuesSet = configSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,14 +281,16 @@ func findConfigurationConflicts(config map[string]interface{}, flags *flag.FlagS
|
||||||
|
|
||||||
// 2. Discard values that implement NamedOption.
|
// 2. Discard values that implement NamedOption.
|
||||||
// Their configuration name differs from their flag name, like `labels` and `label`.
|
// Their configuration name differs from their flag name, like `labels` and `label`.
|
||||||
unknownNamedConflicts := func(f *flag.Flag) {
|
if len(unknownKeys) > 0 {
|
||||||
if namedOption, ok := f.Value.(opts.NamedOption); ok {
|
unknownNamedConflicts := func(f *flag.Flag) {
|
||||||
if _, valid := unknownKeys[namedOption.Name()]; valid {
|
if namedOption, ok := f.Value.(opts.NamedOption); ok {
|
||||||
delete(unknownKeys, namedOption.Name())
|
if _, valid := unknownKeys[namedOption.Name()]; valid {
|
||||||
|
delete(unknownKeys, namedOption.Name())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
flags.VisitAll(unknownNamedConflicts)
|
||||||
}
|
}
|
||||||
flags.VisitAll(unknownNamedConflicts)
|
|
||||||
|
|
||||||
if len(unknownKeys) > 0 {
|
if len(unknownKeys) > 0 {
|
||||||
var unknown []string
|
var unknown []string
|
||||||
|
|
|
@ -291,3 +291,80 @@ func TestLoadDaemonConfigWithMapOptions(t *testing.T) {
|
||||||
t.Fatalf("expected log tag `test`, got %s", tag)
|
t.Fatalf("expected log tag `test`, got %s", tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadDaemonConfigWithTrueDefaultValues(t *testing.T) {
|
||||||
|
c := &daemon.Config{}
|
||||||
|
common := &cli.CommonFlags{}
|
||||||
|
flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
|
||||||
|
flags.BoolVar(&c.EnableUserlandProxy, []string{"-userland-proxy"}, true, "")
|
||||||
|
|
||||||
|
f, err := ioutil.TempFile("", "docker-config-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := flags.ParseFlags([]string{}, false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := f.Name()
|
||||||
|
f.Write([]byte(`{
|
||||||
|
"userland-proxy": false
|
||||||
|
}`))
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if loadedConfig == nil {
|
||||||
|
t.Fatal("expected configuration, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if loadedConfig.EnableUserlandProxy {
|
||||||
|
t.Fatal("expected userland proxy to be disabled, got enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure reloading doesn't generate configuration
|
||||||
|
// conflicts after normalizing boolean values.
|
||||||
|
err = daemon.ReloadConfiguration(configFile, flags, func(reloadedConfig *daemon.Config) {
|
||||||
|
if reloadedConfig.EnableUserlandProxy {
|
||||||
|
t.Fatal("expected userland proxy to be disabled, got enabled")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadDaemonConfigWithTrueDefaultValuesLeaveDefaults(t *testing.T) {
|
||||||
|
c := &daemon.Config{}
|
||||||
|
common := &cli.CommonFlags{}
|
||||||
|
flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
|
||||||
|
flags.BoolVar(&c.EnableUserlandProxy, []string{"-userland-proxy"}, true, "")
|
||||||
|
|
||||||
|
f, err := ioutil.TempFile("", "docker-config-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := flags.ParseFlags([]string{}, false); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile := f.Name()
|
||||||
|
f.Write([]byte(`{}`))
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if loadedConfig == nil {
|
||||||
|
t.Fatal("expected configuration, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !loadedConfig.EnableUserlandProxy {
|
||||||
|
t.Fatal("expected userland proxy to be enabled, got disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
apiserver "github.com/docker/docker/api/server"
|
apiserver "github.com/docker/docker/api/server"
|
||||||
"github.com/docker/docker/daemon"
|
"github.com/docker/docker/daemon"
|
||||||
"github.com/docker/docker/pkg/mflag"
|
"github.com/docker/docker/pkg/mflag"
|
||||||
|
@ -58,7 +59,9 @@ func setupConfigReloadTrap(configFile string, flags *mflag.FlagSet, reload func(
|
||||||
signal.Notify(c, syscall.SIGHUP)
|
signal.Notify(c, syscall.SIGHUP)
|
||||||
go func() {
|
go func() {
|
||||||
for range c {
|
for range c {
|
||||||
daemon.ReloadConfiguration(configFile, flags, reload)
|
if err := daemon.ReloadConfiguration(configFile, flags, reload); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,9 @@ func setupConfigReloadTrap(configFile string, flags *mflag.FlagSet, reload func(
|
||||||
logrus.Debugf("Config reload - waiting signal at %s", ev)
|
logrus.Debugf("Config reload - waiting signal at %s", ev)
|
||||||
for {
|
for {
|
||||||
syscall.WaitForSingleObject(h, syscall.INFINITE)
|
syscall.WaitForSingleObject(h, syscall.INFINITE)
|
||||||
daemon.ReloadConfiguration(configFile, flags, reload)
|
if err := daemon.ReloadConfiguration(configFile, flags, reload); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
Loading…
Reference in a new issue