diff --git a/changelog/unreleased/dump-config.md b/changelog/unreleased/dump-config.md new file mode 100644 index 0000000000..a711ebc5d9 --- /dev/null +++ b/changelog/unreleased/dump-config.md @@ -0,0 +1,7 @@ +Enhancement: Dump reva config on SIGUSR1 + +Add an option to the runtime to dump the configuration on a file +(default to `/tmp/reva-dump.toml` and configurable) when the process +receives a SIGUSR1 signal. Eventual errors are logged in the log. + +https://github.com/cs3org/reva/pull/4031 diff --git a/cmd/revad/main.go b/cmd/revad/main.go index 991073ace7..aad02a6baf 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -115,6 +115,8 @@ func handleSignalFlag() { signal = syscall.SIGQUIT case "stop": signal = syscall.SIGTERM + case "dump": + signal = syscall.SIGUSR1 default: fmt.Fprintf(os.Stderr, "unknown signal %q\n", *signalFlag) os.Exit(1) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 846b03374e..432b2044ab 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -21,6 +21,8 @@ package config import ( "fmt" "io" + "os" + "path/filepath" "reflect" "github.com/BurntSushi/toml" @@ -59,6 +61,7 @@ type Shared struct { // Core holds the core configuration. type Core struct { MaxCPUs string `key:"max_cpus" mapstructure:"max_cpus"` + ConfigDumpFile string `key:"config_dump_file" mapstructure:"config_dump_file"` TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled" default:"true"` TracingEndpoint string `key:"tracing_endpoint" mapstructure:"tracing_endpoint" default:"localhost:6831"` TracingCollector string `key:"tracing_collector" mapstructure:"tracing_collector"` @@ -78,12 +81,19 @@ type Lookuper interface { Lookup(key string) (any, error) } +func (c *Config) init() { + if c.Core.ConfigDumpFile == "" { + c.Core.ConfigDumpFile = filepath.Join(os.TempDir(), "reva-dump.toml") + } +} + // Load loads the configuration from the reader. func Load(r io.Reader) (*Config, error) { var c Config if err := defaults.Set(&c); err != nil { return nil, err } + c.init() var raw map[string]any if _, err := toml.NewDecoder(r).Decode(&raw); err != nil { return nil, errors.Wrap(err, "config: error decoding toml data") diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index 7ab22f8a03..3d4385c0db 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -248,6 +248,7 @@ nats_token = "secret-token-example"` MaxCPUs: "1", TracingEnabled: true, TracingEndpoint: "localhost:6831", + ConfigDumpFile: "/tmp/reva-dump.toml", }, c2.Core) assert.Equal(t, Vars{ @@ -500,6 +501,7 @@ func TestDump(t *testing.T) { "tracing_collector": "", "tracing_service_name": "", "tracing_service": "", + "config_dump_file": "", }, "vars": map[string]any{ "db_username": "root", diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index d895b122f1..3542715572 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -22,10 +22,14 @@ import ( "context" "fmt" "net" + "os" + "os/signal" "runtime" "strconv" "strings" + "syscall" + "github.com/BurntSushi/toml" "github.com/pkg/errors" "github.com/cs3org/reva/cmd/revad/pkg/config" @@ -121,7 +125,7 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { return nil, err } - return &Reva{ + r := &Reva{ ctx: ctx, config: config, servers: servers, @@ -130,7 +134,40 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { lns: listeners, pidfile: opts.PidFile, log: log, - }, nil + } + r.initConfigDumper() + return r, nil +} + +func (r *Reva) initConfigDumper() { + // dump the config when the process receives a SIGUSR1 signal + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGUSR1) + + go func() { + for { + <-sigs + r.dumpConfig() + } + }() +} + +func (r *Reva) dumpConfig() { + cfg := r.config.Dump() + out := r.config.Core.ConfigDumpFile + f, err := os.OpenFile(out, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) + if err != nil { + r.log.Error().Err(err).Msgf("error opening file %s for dumping the config", out) + return + } + defer f.Close() + + enc := toml.NewEncoder(f) + if err := enc.Encode(cfg); err != nil { + r.log.Error().Err(err).Msg("error encoding config") + return + } + r.log.Debug().Msgf("config dumped successfully in %s", out) } func servicesAddresses(cfg *config.Config) map[string]grace.Addressable {