diff --git a/cmd/notary-server/config.go b/cmd/notary-server/config.go index bb170b9332..21b3fab28a 100644 --- a/cmd/notary-server/config.go +++ b/cmd/notary-server/config.go @@ -3,12 +3,9 @@ package main import ( "crypto/tls" "fmt" - "os" - "os/signal" "path" "strconv" "strings" - "syscall" "time" "github.com/Sirupsen/logrus" @@ -290,31 +287,3 @@ func parseServerConfig(configFilePath string, hRegister healthRegister, doBootst ConsistentCacheControlConfig: consistentCache, }, nil } - -func setupSignalTrap() { - c := make(chan os.Signal, 1) - signal.Notify(c, notary.NotarySupportedSignals...) - go func() { - for { - signalHandle(<-c) - } - }() -} - -// signalHandle will increase/decrease the logging level via the signal we get. -func signalHandle(sig os.Signal) { - switch sig { - case syscall.SIGUSR1: - if err := utils.AdjustLogLevel(true); err != nil { - fmt.Printf("Attempt to increase log level failed, will remain at %s level, error: %s\n", logrus.GetLevel(), err) - return - } - case syscall.SIGUSR2: - if err := utils.AdjustLogLevel(false); err != nil { - fmt.Printf("Attempt to decrease log level failed, will remain at %s level, error: %s\n", logrus.GetLevel(), err) - return - } - } - - fmt.Println("Successfully setting log level to ", logrus.GetLevel()) -} diff --git a/cmd/notary-server/main.go b/cmd/notary-server/main.go index c2cdf56e75..232e486c72 100644 --- a/cmd/notary-server/main.go +++ b/cmd/notary-server/main.go @@ -7,10 +7,12 @@ import ( "net/http" _ "net/http/pprof" "os" + "os/signal" "github.com/Sirupsen/logrus" "github.com/docker/distribution/health" "github.com/docker/notary/server" + "github.com/docker/notary/utils" "github.com/docker/notary/version" ) @@ -61,7 +63,10 @@ func main() { logrus.Fatal(err.Error()) } - setupSignalTrap() + c := utils.SetupSignalTrap(utils.LogLevelSignalHandle) + if c != nil { + defer signal.Stop(c) + } if flagStorage.doBootstrap { err = bootstrap(ctx) diff --git a/cmd/notary-server/main_test.go b/cmd/notary-server/main_test.go index 14fbd69bc7..4bcc0c3fbe 100644 --- a/cmd/notary-server/main_test.go +++ b/cmd/notary-server/main_test.go @@ -6,14 +6,11 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" "reflect" "strings" - "syscall" "testing" "time" - "github.com/Sirupsen/logrus" "github.com/docker/distribution/health" "github.com/docker/notary" "github.com/docker/notary/server/storage" @@ -416,30 +413,3 @@ func TestSampleConfig(t *testing.T) { // once for the DB, once for the trust service require.Equal(t, registerCalled, 2) } - -func TestSignalHandle(t *testing.T) { - tempdir, err := ioutil.TempDir("", "test-signal-handle") - require.NoError(t, err) - defer os.RemoveAll(tempdir) - f, err := os.Create(filepath.Join(tempdir, "testSignalHandle.json")) - require.NoError(t, err) - - f.WriteString(`{"logging": {"level": "info"}}`) - - v := viper.New() - utils.SetupViper(v, "envPrefix") - err = utils.ParseViper(v, f.Name()) - require.NoError(t, err) - - // Info + SIGUSR1 -> Debug - signalHandle(syscall.SIGUSR1) - require.Equal(t, logrus.GetLevel(), logrus.DebugLevel) - - // Debug + SIGUSR1 -> Debug - signalHandle(syscall.SIGUSR1) - require.Equal(t, logrus.GetLevel(), logrus.DebugLevel) - - // Debug + SIGUSR2-> Info - signalHandle(syscall.SIGUSR2) - require.Equal(t, logrus.GetLevel(), logrus.InfoLevel) -} diff --git a/cmd/notary-signer/main.go b/cmd/notary-signer/main.go index d15f124ae9..d7459fc62d 100644 --- a/cmd/notary-signer/main.go +++ b/cmd/notary-signer/main.go @@ -6,8 +6,10 @@ import ( "log" "net/http" "os" + "os/signal" "github.com/Sirupsen/logrus" + "github.com/docker/notary/utils" "github.com/docker/notary/version" _ "github.com/go-sql-driver/mysql" ) @@ -66,6 +68,11 @@ func main() { log.Println("RPC server listening on", signerConfig.GRPCAddr) } + c := utils.SetupSignalTrap(utils.LogLevelSignalHandle) + if c != nil { + defer signal.Stop(c) + } + grpcServer.Serve(lis) } diff --git a/const.go b/const.go index ffc408647e..0096b3a27f 100644 --- a/const.go +++ b/const.go @@ -1,10 +1,6 @@ package notary -import ( - "os" - "syscall" - "time" -) +import "time" // application wide constants const ( @@ -81,11 +77,3 @@ var NotaryDefaultExpiries = map[string]time.Duration{ "snapshot": NotarySnapshotExpiry, "timestamp": NotaryTimestampExpiry, } - -// NotarySupportedSignals contains the signals we would like to capture: -// - SIGUSR1, indicates a increment of the log level. -// - SIGUSR2, indicates a decrement of the log level. -var NotarySupportedSignals = []os.Signal{ - syscall.SIGUSR1, - syscall.SIGUSR2, -} diff --git a/const_nowindows.go b/const_nowindows.go new file mode 100644 index 0000000000..67551717a5 --- /dev/null +++ b/const_nowindows.go @@ -0,0 +1,16 @@ +// +build !windows + +package notary + +import ( + "os" + "syscall" +) + +// NotarySupportedSignals contains the signals we would like to capture: +// - SIGUSR1, indicates a increment of the log level. +// - SIGUSR2, indicates a decrement of the log level. +var NotarySupportedSignals = []os.Signal{ + syscall.SIGUSR1, + syscall.SIGUSR2, +} diff --git a/const_windows.go b/const_windows.go new file mode 100644 index 0000000000..e2dff0e4b5 --- /dev/null +++ b/const_windows.go @@ -0,0 +1,8 @@ +// +build windows + +package notary + +import "os" + +// NotarySupportedSignals does not contain any signals, because SIGUSR1/2 are not supported on windows +var NotarySupportedSignals = []os.Signal{} diff --git a/docs/reference/signer-config.md b/docs/reference/signer-config.md index 7ced28e175..ce95934966 100644 --- a/docs/reference/signer-config.md +++ b/docs/reference/signer-config.md @@ -210,6 +210,42 @@ The environment variables for the older passwords are optional, but Notary Signer will not be able to decrypt older keys if they are not provided, and attempts to sign data using those keys will fail. +## Hot logging level reload +We don't support completely reloading notary signer configuration files yet at present. What we support for now is: +- increase logging level by signaling `SIGUSR1` +- decrease logging level by signaling `SIGUSR2` + +Example: + +To increase logging level +``` +$ kill -s SIGUSR1 PID + +or + +$ docker exec -i CONTAINER_ID kill -s SIGUSR1 PID +``` + +To decrease logging level +``` +$ kill -s SIGUSR2 PID + +or + +$ docker exec -i CONTAINER_ID kill -s SIGUSR2 PID +``` +PID is the process id of `notary-signer` and it may not the PID 1 process if you are running +the container with some kind of wrapper startup script or something. + +You can get the PID of `notary-signer` through +``` +$ docker exec CONTAINER_ID ps aux + +or + +$ ps aux | grep "notary-signer -config" | grep -v "grep" +``` + ## Related information diff --git a/utils/configuration.go b/utils/configuration.go index f94b73a270..cc97810da9 100644 --- a/utils/configuration.go +++ b/utils/configuration.go @@ -5,6 +5,8 @@ package utils import ( "crypto/tls" "fmt" + "os" + "os/signal" "path/filepath" "strings" @@ -244,3 +246,20 @@ func AdjustLogLevel(increment bool) error { logrus.SetLevel(lvl) return nil } + +// SetupSignalTrap is a utility to trap supported signals hand handle them (currently by increasing logging) +func SetupSignalTrap(handler func(os.Signal)) chan os.Signal { + if len(notary.NotarySupportedSignals) == 0 { + return nil + + } + c := make(chan os.Signal, 1) + signal.Notify(c, notary.NotarySupportedSignals...) + go func() { + for { + handler(<-c) + } + }() + + return c +} diff --git a/utils/configuration_nowindows.go b/utils/configuration_nowindows.go new file mode 100644 index 0000000000..22c7528dd1 --- /dev/null +++ b/utils/configuration_nowindows.go @@ -0,0 +1,29 @@ +// +build !windows + +package utils + +import ( + "fmt" + "os" + "syscall" + + "github.com/Sirupsen/logrus" +) + +// LogLevelSignalHandle will increase/decrease the logging level via the signal we get. +func LogLevelSignalHandle(sig os.Signal) { + switch sig { + case syscall.SIGUSR1: + if err := AdjustLogLevel(true); err != nil { + fmt.Printf("Attempt to increase log level failed, will remain at %s level, error: %s\n", logrus.GetLevel(), err) + return + } + case syscall.SIGUSR2: + if err := AdjustLogLevel(false); err != nil { + fmt.Printf("Attempt to decrease log level failed, will remain at %s level, error: %s\n", logrus.GetLevel(), err) + return + } + } + + fmt.Println("Successfully setting log level to ", logrus.GetLevel()) +} diff --git a/utils/configuration_nowindows_test.go b/utils/configuration_nowindows_test.go new file mode 100644 index 0000000000..d414af8fc1 --- /dev/null +++ b/utils/configuration_nowindows_test.go @@ -0,0 +1,33 @@ +// +build !windows + +package utils + +import ( + "io/ioutil" + "os" + "syscall" + "testing" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestLogLevelSignalHandle(t *testing.T) { + tempdir, err := ioutil.TempDir("", "test-signal-handle") + require.NoError(t, err) + defer os.RemoveAll(tempdir) + + logrus.SetLevel(logrus.InfoLevel) + + // Info + SIGUSR1 -> Debug + LogLevelSignalHandle(syscall.SIGUSR1) + require.Equal(t, logrus.GetLevel(), logrus.DebugLevel) + + // Debug + SIGUSR1 -> Debug + LogLevelSignalHandle(syscall.SIGUSR1) + require.Equal(t, logrus.GetLevel(), logrus.DebugLevel) + + // Debug + SIGUSR2-> Info + LogLevelSignalHandle(syscall.SIGUSR2) + require.Equal(t, logrus.GetLevel(), logrus.InfoLevel) +} diff --git a/utils/configuration_test.go b/utils/configuration_test.go index bf0c5f71a2..7dbaf859d3 100644 --- a/utils/configuration_test.go +++ b/utils/configuration_test.go @@ -6,8 +6,10 @@ import ( "fmt" "io/ioutil" "os" + "os/signal" "path/filepath" "reflect" + "syscall" "testing" "github.com/Sirupsen/logrus" @@ -560,3 +562,28 @@ func TestAdjustLogLevel(t *testing.T) { err = AdjustLogLevel(optDecrement) require.Error(t, err) } + +func TestSetSignalTrap(t *testing.T) { + var signalsPassedOn map[string]struct{} + + signalHandler := func(s os.Signal) { + signalsPassedOn := make(map[string]struct{}) + signalsPassedOn[s.String()] = struct{}{} + } + c := SetupSignalTrap(signalHandler) + + if len(notary.NotarySupportedSignals) == 0 { // currently, windows only + require.Nil(t, c) + } else { + require.NotNil(t, c) + defer signal.Stop(c) + } + + for _, s := range notary.NotarySupportedSignals { + syscallSignal, ok := s.(syscall.Signal) + require.True(t, ok) + require.NoError(t, syscall.Kill(syscall.Getpid(), syscallSignal)) + require.Len(t, signalsPassedOn, 0) + require.NotNil(t, signalsPassedOn[s.String()]) + } +} diff --git a/utils/configuration_windows.go b/utils/configuration_windows.go new file mode 100644 index 0000000000..bb43c7e3b1 --- /dev/null +++ b/utils/configuration_windows.go @@ -0,0 +1,9 @@ +// +build windows + +package utils + +import "os" + +// LogLevelSignalHandle will do nothing, because we aren't currently supporting signal handling in windows +func LogLevelSignalHandle(sig os.Signal) { +}