diff --git a/cmd/notary-server/config.go b/cmd/notary-server/config.go index 0177be6a36..2845ec5204 100644 --- a/cmd/notary-server/config.go +++ b/cmd/notary-server/config.go @@ -6,6 +6,9 @@ import ( "path" "strconv" "strings" + "os" + "os/signal" + "syscall" "time" "github.com/Sirupsen/logrus" @@ -225,6 +228,7 @@ func parseServerConfig(configFilePath string, hRegister healthRegister) (context } ctx := context.Background() + ctx = context.WithValue(ctx, "config", config) // default is error level lvl, err := utils.ParseLogLevel(config, logrus.ErrorLevel) @@ -278,3 +282,37 @@ func parseServerConfig(configFilePath string, hRegister healthRegister) (context ConsistentCacheControlConfig: consistentCache, }, nil } + +func setupConfigReloadTrap(oldConfig *viper.Viper, configFile string, envPrefix string) { + c := make(chan os.Signal, 1) + signal.Notify(c, notary.NotarySupportedSignals...) + go func() { + for { + reloadConfig(<-c, configFile, oldConfig, envPrefix) + } + }() +} + +// reloadConfig was designed to do all the dirty work that needed to reload +// the config for notary-server and make the entrance-ReloadConfiguration() +// for hot configuration reload do some generic things. +func reloadConfig(sig os.Signal, configFile string, oldConfig *viper.Viper, envPrefix string) { + reload := func(config *viper.Viper) { + switch sig { + case syscall.SIGUSR1: + if err := utils.AdjustLogLevel(true); err != nil { + fmt.Println("Increase log level failed, will use the old one, error: ", err) + return + } + case syscall.SIGUSR2: + if err := utils.AdjustLogLevel(false); err != nil { + fmt.Println("Decrease log level failed, will use the old one, error: ", err) + return + } + } + + fmt.Println("Successfully setting log level to ", logrus.GetLevel()) + } + + utils.ReloadConfiguration(oldConfig, configFile, envPrefix, reload) +} diff --git a/cmd/notary-server/main.go b/cmd/notary-server/main.go index 62042e4df9..6f7cc22ae9 100644 --- a/cmd/notary-server/main.go +++ b/cmd/notary-server/main.go @@ -12,6 +12,7 @@ import ( "github.com/docker/distribution/health" "github.com/docker/notary/server" "github.com/docker/notary/version" + "github.com/spf13/viper" ) // DebugAddress is the debug server address to listen on @@ -57,6 +58,12 @@ func main() { logrus.Fatal(err.Error()) } + config, ok := ctx.Value("config").(*viper.Viper) + if !ok { + logrus.Fatalf("parse config failed") + } + setupConfigReloadTrap(config, configFile, envPrefix) + if doBootstrap { err = bootstrap(ctx) } else { diff --git a/utils/configuration.go b/utils/configuration.go index f94b73a270..a59d536439 100644 --- a/utils/configuration.go +++ b/utils/configuration.go @@ -244,3 +244,31 @@ func AdjustLogLevel(increment bool) error { logrus.SetLevel(lvl) return nil } + +// ReloadConfiguration reads and parse the configuration in the host and do some verification. +func ReloadConfiguration(oldConfig *viper.Viper, configFile string, envPrefix string, reload func(*viper.Viper)) error { + logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile) + + // parse a viper as the new Config + newConfig := viper.New() + SetupViper(newConfig, envPrefix) + if err := ParseViper(newConfig, configFile); err != nil { + return err + } + + // To check if there was anything wrong + if err := validateConfiguration(oldConfig, newConfig); err != nil { + return err + } + + reload(newConfig) + return nil +} + +// Reserved for future use, we can use it to validate some specific configs when necessary. +// For example: +// - to check if there are conflicts. +// - to do some sanity check maybe. +func validateConfiguration(oldConfig, newConfig *viper.Viper) error { + return nil +}