From 9be2903a34df94a1cd380a03f04e6b9bde9ca5a6 Mon Sep 17 00:00:00 2001 From: Anton Averchenkov <84287187+averche@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:57:45 -0400 Subject: [PATCH] agent: Don't restart process unless environment variables changed (#20917) --- command/agent/exec/exec.go | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/command/agent/exec/exec.go b/command/agent/exec/exec.go index 1d924f8c97..7b4ee3e35f 100644 --- a/command/agent/exec/exec.go +++ b/command/agent/exec/exec.go @@ -5,12 +5,14 @@ import ( "fmt" "io" "os" + "sort" "time" "github.com/hashicorp/consul-template/child" ctconfig "github.com/hashicorp/consul-template/config" "github.com/hashicorp/consul-template/manager" "github.com/hashicorp/go-hclog" + "golang.org/x/exp/slices" "github.com/hashicorp/vault/command/agent/config" "github.com/hashicorp/vault/command/agent/internal/ctmanager" @@ -69,6 +71,10 @@ type Server struct { // child process each time we restart it. // this function closes the old watcher go-routine so it doesn't leak childProcessExitCodeCloser func() + + // lastRenderedEnvVars is the cached value of all environment variables + // rendered by the templating engine; it is used for detecting changes + lastRenderedEnvVars []string } type ProcessExitError struct { @@ -179,7 +185,7 @@ func (s *Server) Run(ctx context.Context, incomingVaultToken chan string) error go s.runner.Start() case <-s.runner.TemplateRenderedCh(): // A template has been rendered, figure out what to do - s.logger.Debug("template rendered") + s.logger.Trace("template rendered") events := s.runner.RenderEvents() // This checks if we've finished rendering the initial set of templates, @@ -204,13 +210,28 @@ func (s *Server) Run(ctx context.Context, incomingVaultToken chan string) error } } } - - if doneRendering { - s.logger.Debug("done rendering templates/detected change, bouncing process") - if err := s.bounceCmd(renderedEnvVars); err != nil { - return fmt.Errorf("unable to bounce command: %w", err) - } + if !doneRendering { + continue } + + // sort the environment variables for a deterministic output and easy comparison + sort.Strings(renderedEnvVars) + + s.logger.Trace("done rendering templates") + + // don't restart the process unless a change is detected + if slices.Equal(s.lastRenderedEnvVars, renderedEnvVars) { + continue + } + + s.lastRenderedEnvVars = renderedEnvVars + + s.logger.Debug("detected a change in the environment variables: restarting the child process") + + if err := s.bounceCmd(renderedEnvVars); err != nil { + return fmt.Errorf("unable to bounce command: %w", err) + } + case exitCode := <-s.childProcessExitCh: // process exited on its own return &ProcessExitError{ExitCode: exitCode}