New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add reload capability for Vault listener certs #1196
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
7e52796
Add reload capability for Vault listener certs. No tests (other than
jefferai 6430cd9
Add tests. This actually adds the initial tests for the TLS listener,
jefferai 9e49463
Fix typo
jefferai 9f2f5b1
Retool to have reloading logic run in command/server
jefferai 92088f0
For not shutdown triggered...
jefferai ca40e06
Don't inline factory
jefferai 14f5385
Don't generate an ID; use address for the ID. Generally speaking we'l…
jefferai 0c56385
Properly scope config objects for reloading
jefferai 3a878c3
Add test for listener reloading, and update website docs.
jefferai File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,13 +8,16 @@ import ( | |
"net/http" | ||
"net/url" | ||
"os" | ||
"os/signal" | ||
"runtime" | ||
"sort" | ||
"strconv" | ||
"strings" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/armon/go-metrics" | ||
"github.com/hashicorp/go-multierror" | ||
"github.com/hashicorp/logutils" | ||
"github.com/hashicorp/vault/audit" | ||
"github.com/hashicorp/vault/command/server" | ||
|
@@ -34,8 +37,12 @@ type ServerCommand struct { | |
CredentialBackends map[string]logical.Factory | ||
LogicalBackends map[string]logical.Factory | ||
|
||
ShutdownCh <-chan struct{} | ||
ShutdownCh chan struct{} | ||
SighupCh chan struct{} | ||
|
||
Meta | ||
|
||
ReloadFuncs map[string][]server.ReloadFunc | ||
} | ||
|
||
func (c *ServerCommand) Run(args []string) int { | ||
|
@@ -274,7 +281,7 @@ func (c *ServerCommand) Run(args []string) int { | |
// Initialize the listeners | ||
lns := make([]net.Listener, 0, len(config.Listeners)) | ||
for i, lnConfig := range config.Listeners { | ||
ln, props, err := server.NewListener(lnConfig.Type, lnConfig.Config) | ||
ln, props, reloadFunc, err := server.NewListener(lnConfig.Type, lnConfig.Config) | ||
if err != nil { | ||
c.Ui.Error(fmt.Sprintf( | ||
"Error initializing listener of type %s: %s", | ||
|
@@ -295,6 +302,12 @@ func (c *ServerCommand) Run(args []string) int { | |
"%s (%s)", lnConfig.Type, strings.Join(propsList, ", ")) | ||
|
||
lns = append(lns, ln) | ||
|
||
if reloadFunc != nil { | ||
relSlice := c.ReloadFuncs["listener|"+lnConfig.Type] | ||
relSlice = append(relSlice, reloadFunc) | ||
c.ReloadFuncs["listener|"+lnConfig.Type] = relSlice | ||
} | ||
} | ||
|
||
infoKeys = append(infoKeys, "version") | ||
|
@@ -333,11 +346,20 @@ func (c *ServerCommand) Run(args []string) int { | |
logGate.Flush() | ||
|
||
// Wait for shutdown | ||
select { | ||
case <-c.ShutdownCh: | ||
c.Ui.Output("==> Vault shutdown triggered") | ||
if err := core.Shutdown(); err != nil { | ||
c.Ui.Error(fmt.Sprintf("Error with core shutdown: %s", err)) | ||
shutdownTriggered := false | ||
for !shutdownTriggered { | ||
select { | ||
case <-c.ShutdownCh: | ||
c.Ui.Output("==> Vault shutdown triggered") | ||
if err := core.Shutdown(); err != nil { | ||
c.Ui.Error(fmt.Sprintf("Error with core shutdown: %s", err)) | ||
} | ||
shutdownTriggered = true | ||
case <-c.SighupCh: | ||
c.Ui.Output("==> Vault reload triggered") | ||
if err := c.Reload(configPath); err != nil { | ||
c.Ui.Error(fmt.Sprintf("Error(s) were encountered during reload: %s", err)) | ||
} | ||
} | ||
} | ||
return 0 | ||
|
@@ -530,6 +552,46 @@ func (c *ServerCommand) setupTelementry(config *server.Config) error { | |
return nil | ||
} | ||
|
||
func (c *ServerCommand) Reload(configPath []string) error { | ||
// Read the new config | ||
var config *server.Config | ||
for _, path := range configPath { | ||
current, err := server.LoadConfig(path) | ||
if err != nil { | ||
retErr := fmt.Errorf("Error loading configuration from %s: %s", path, err) | ||
c.Ui.Error(retErr.Error()) | ||
return retErr | ||
} | ||
|
||
if config == nil { | ||
config = current | ||
} else { | ||
config = config.Merge(current) | ||
} | ||
} | ||
|
||
// Ensure at least one config was found. | ||
if config == nil { | ||
retErr := fmt.Errorf("No configuration files found") | ||
c.Ui.Error(retErr.Error()) | ||
return retErr | ||
} | ||
|
||
var reloadErrors *multierror.Error | ||
// Call reload on the listeners. This will call each listener with each | ||
// config block, but they verify the address. | ||
for _, lnConfig := range config.Listeners { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to range over the listeners? It seems like we have one reloadFunc per anyways, seems like we can just loop over those |
||
for _, relFunc := range c.ReloadFuncs["listener|"+lnConfig.Type] { | ||
if err := relFunc(lnConfig.Config); err != nil { | ||
retErr := fmt.Errorf("Error encountered reloading configuration: %s", err) | ||
reloadErrors = multierror.Append(retErr) | ||
} | ||
} | ||
} | ||
|
||
return reloadErrors.ErrorOrNil() | ||
} | ||
|
||
func (c *ServerCommand) Synopsis() string { | ||
return "Start a Vault server" | ||
} | ||
|
@@ -577,3 +639,37 @@ General Options: | |
` | ||
return strings.TrimSpace(helpText) | ||
} | ||
|
||
// MakeShutdownCh returns a channel that can be used for shutdown | ||
// notifications for commands. This channel will send a message for every | ||
// interrupt or SIGTERM received. | ||
func MakeShutdownCh() chan struct{} { | ||
resultCh := make(chan struct{}) | ||
|
||
signalCh := make(chan os.Signal, 4) | ||
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM) | ||
go func() { | ||
for { | ||
<-signalCh | ||
resultCh <- struct{}{} | ||
} | ||
}() | ||
return resultCh | ||
} | ||
|
||
// MakeSighupCh returns a channel that can be used for SIGHUP | ||
// reloading. This channel will send a message for every | ||
// SIGHUP received. | ||
func MakeSighupCh() chan struct{} { | ||
resultCh := make(chan struct{}) | ||
|
||
signalCh := make(chan os.Signal, 4) | ||
signal.Notify(signalCh, os.Interrupt, syscall.SIGHUP) | ||
go func() { | ||
for { | ||
<-signalCh | ||
resultCh <- struct{}{} | ||
} | ||
}() | ||
return resultCh | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to expose this method?
Also, is it possible to use this method to load the configuration the first time as well?