mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
Fix non-JSON log messages when using -log-format JSON (#24252)
* Fix non-JSON log messages when using -log-format JSON Removed the call to consul-template's logging.Setup inside the created of config for the Runner. Instead we call it when we assign the logger to the Agent command. * The elusive extra line * Adjust the approach * changelog * Infer levels *with* timestamp prefix * InferLeveslWithTimestamp required InferLevels * Test to show -log-format and -log-file working in consul-template generated messages * classic typo --------- Co-authored-by: Violet Hynes <violet.hynes@hashicorp.com>
This commit is contained in:
3
changelog/24252.txt
Normal file
3
changelog/24252.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
agent/logging: Agent should now honor correct -log-format and -log-file settings in logs generated by the consul-template library.
|
||||||
|
```
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
systemd "github.com/coreos/go-systemd/daemon"
|
systemd "github.com/coreos/go-systemd/daemon"
|
||||||
ctconfig "github.com/hashicorp/consul-template/config"
|
ctconfig "github.com/hashicorp/consul-template/config"
|
||||||
log "github.com/hashicorp/go-hclog"
|
hclog "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/go-secure-stdlib/gatedwriter"
|
"github.com/hashicorp/go-secure-stdlib/gatedwriter"
|
||||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||||
@@ -78,7 +78,7 @@ type AgentCommand struct {
|
|||||||
|
|
||||||
logWriter io.Writer
|
logWriter io.Writer
|
||||||
logGate *gatedwriter.Writer
|
logGate *gatedwriter.Writer
|
||||||
logger log.Logger
|
logger hclog.Logger
|
||||||
|
|
||||||
// Telemetry object
|
// Telemetry object
|
||||||
metricsHelper *metricsutil.MetricsHelper
|
metricsHelper *metricsutil.MetricsHelper
|
||||||
@@ -210,7 +210,16 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
c.outputErrors(err)
|
c.outputErrors(err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the logger and then base the log writer on that logger.
|
||||||
|
// Log writer is supplied to consul-template runners for templates and execs.
|
||||||
|
// We want to ensure that consul-template will honor the settings, for example
|
||||||
|
// if the -log-format is JSON we want JSON, not a mix of JSON and non-JSON messages.
|
||||||
c.logger = l
|
c.logger = l
|
||||||
|
c.logWriter = l.StandardWriter(&hclog.StandardLoggerOptions{
|
||||||
|
InferLevels: true,
|
||||||
|
InferLevelsWithTimestamp: true,
|
||||||
|
})
|
||||||
|
|
||||||
infoKeys := make([]string, 0, 10)
|
infoKeys := make([]string, 0, 10)
|
||||||
info := make(map[string]string)
|
info := make(map[string]string)
|
||||||
@@ -1093,31 +1102,31 @@ func (c *AgentCommand) handleQuit(enabled bool) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newLogger creates a logger based on parsed config field on the Agent Command struct.
|
// newLogger creates a logger based on parsed config field on the Agent Command struct.
|
||||||
func (c *AgentCommand) newLogger() (log.InterceptLogger, error) {
|
func (c *AgentCommand) newLogger() (hclog.InterceptLogger, error) {
|
||||||
if c.config == nil {
|
if c.config == nil {
|
||||||
return nil, fmt.Errorf("cannot create logger, no config")
|
return nil, fmt.Errorf("cannot create logger, no config")
|
||||||
}
|
}
|
||||||
|
|
||||||
var errors error
|
var errs *multierror.Error
|
||||||
|
|
||||||
// Parse all the log related config
|
// Parse all the log related config
|
||||||
logLevel, err := logging.ParseLogLevel(c.config.LogLevel)
|
logLevel, err := logging.ParseLogLevel(c.config.LogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = multierror.Append(errors, err)
|
errs = multierror.Append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logFormat, err := logging.ParseLogFormat(c.config.LogFormat)
|
logFormat, err := logging.ParseLogFormat(c.config.LogFormat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = multierror.Append(errors, err)
|
errs = multierror.Append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logRotateDuration, err := parseutil.ParseDurationSecond(c.config.LogRotateDuration)
|
logRotateDuration, err := parseutil.ParseDurationSecond(c.config.LogRotateDuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = multierror.Append(errors, err)
|
errs = multierror.Append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors != nil {
|
if errs != nil {
|
||||||
return nil, errors
|
return nil, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
logCfg := &logging.LogConfig{
|
logCfg := &logging.LogConfig{
|
||||||
@@ -1140,20 +1149,20 @@ func (c *AgentCommand) newLogger() (log.InterceptLogger, error) {
|
|||||||
|
|
||||||
// loadConfig attempts to generate an Agent config from the file(s) specified.
|
// loadConfig attempts to generate an Agent config from the file(s) specified.
|
||||||
func (c *AgentCommand) loadConfig(paths []string) (*agentConfig.Config, error) {
|
func (c *AgentCommand) loadConfig(paths []string) (*agentConfig.Config, error) {
|
||||||
var errors error
|
var errs *multierror.Error
|
||||||
cfg := agentConfig.NewConfig()
|
cfg := agentConfig.NewConfig()
|
||||||
|
|
||||||
for _, configPath := range paths {
|
for _, configPath := range paths {
|
||||||
configFromPath, err := agentConfig.LoadConfig(configPath)
|
configFromPath, err := agentConfig.LoadConfig(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = multierror.Append(errors, fmt.Errorf("error loading configuration from %s: %w", configPath, err))
|
errs = multierror.Append(errs, fmt.Errorf("error loading configuration from %s: %w", configPath, err))
|
||||||
} else {
|
} else {
|
||||||
cfg = cfg.Merge(configFromPath)
|
cfg = cfg.Merge(configFromPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors != nil {
|
if errs != nil {
|
||||||
return nil, errors
|
return nil, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cfg.ValidateConfig(); err != nil {
|
if err := cfg.ValidateConfig(); err != nil {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
ctconfig "github.com/hashicorp/consul-template/config"
|
ctconfig "github.com/hashicorp/consul-template/config"
|
||||||
ctlogging "github.com/hashicorp/consul-template/logging"
|
ctlogging "github.com/hashicorp/consul-template/logging"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/command/agent/config"
|
"github.com/hashicorp/vault/command/agent/config"
|
||||||
"github.com/hashicorp/vault/sdk/helper/pointerutil"
|
"github.com/hashicorp/vault/sdk/helper/pointerutil"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -826,7 +827,7 @@ auto_auth {
|
|||||||
verify := func(suffix string) {
|
verify := func(suffix string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
// We need to poll for a bit to give Agent time to render the
|
// We need to poll for a bit to give Agent time to render the
|
||||||
// templates. Without this this, the test will attempt to read
|
// templates. Without this, the test will attempt to read
|
||||||
// the temp dir before Agent has had time to render and will
|
// the temp dir before Agent has had time to render and will
|
||||||
// likely fail the test
|
// likely fail the test
|
||||||
tick := time.Tick(1 * time.Second)
|
tick := time.Tick(1 * time.Second)
|
||||||
@@ -3070,6 +3071,107 @@ vault {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAgent_Logging_ConsulTemplate attempts to ensure two things about Vault Agent logs:
|
||||||
|
// 1. When -log-format command line arg is set to JSON, it is honored as the output format
|
||||||
|
// for messages generated from within the consul-template library.
|
||||||
|
// 2. When -log-file command line arg is supplied, a file receives all log messages
|
||||||
|
// generated by the consul-template library (they don't just go to stdout/stderr).
|
||||||
|
// Should prevent a regression of: https://github.com/hashicorp/vault/issues/21109
|
||||||
|
func TestAgent_Logging_ConsulTemplate(t *testing.T) {
|
||||||
|
const (
|
||||||
|
runnerLogMessage = "(runner) creating new runner (dry: false, once: false)"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configure a Vault server so Agent can successfully communicate and render its templates
|
||||||
|
cluster := minimal.NewTestSoloCluster(t, nil)
|
||||||
|
apiClient := cluster.Cores[0].Client
|
||||||
|
t.Setenv(api.EnvVaultAddress, apiClient.Address())
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
roleIDPath, secretIDPath := setupAppRoleAndKVMounts(t, apiClient)
|
||||||
|
|
||||||
|
// Create relevant configs for Vault Agent (config, template config)
|
||||||
|
templateSrc := filepath.Join(tempDir, "render_1.tmpl")
|
||||||
|
err := os.WriteFile(templateSrc, []byte(templateContents(1)), 0o600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
templateConfig := fmt.Sprintf(templateConfigString, templateSrc, tempDir, "render_1.json")
|
||||||
|
|
||||||
|
config := `
|
||||||
|
vault {
|
||||||
|
address = "%s"
|
||||||
|
tls_skip_verify = true
|
||||||
|
}
|
||||||
|
|
||||||
|
auto_auth {
|
||||||
|
method "approle" {
|
||||||
|
mount_path = "auth/approle"
|
||||||
|
config = {
|
||||||
|
role_id_file_path = "%s"
|
||||||
|
secret_id_file_path = "%s"
|
||||||
|
remove_secret_id_file_after_reading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%s
|
||||||
|
`
|
||||||
|
config = fmt.Sprintf(config, apiClient.Address(), roleIDPath, secretIDPath, templateConfig)
|
||||||
|
configFileName := filepath.Join(tempDir, "config.hcl")
|
||||||
|
err = os.WriteFile(configFileName, []byte(config), 0o600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, cmd := testAgentCommand(t, nil)
|
||||||
|
logFilePath := filepath.Join(tempDir, "agent")
|
||||||
|
|
||||||
|
// Start Vault Agent
|
||||||
|
go func() {
|
||||||
|
code := cmd.Run([]string{"-config", configFileName, "-log-format", "json", "-log-file", logFilePath, "-log-level", "trace"})
|
||||||
|
require.Equalf(t, 0, code, "Vault Agent returned a non-zero exit code")
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-cmd.startedCh:
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatal("timeout starting agent")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give Vault Agent some time to render our template.
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
// This flag will be used to capture whether we saw a consul-template log
|
||||||
|
// message in the log file (the presence of the log file is also part of the test)
|
||||||
|
found := false
|
||||||
|
|
||||||
|
// Vault Agent file logs will match agent-{timestamp}.log based on the
|
||||||
|
// cmd line argument we supplied, e.g. agent-1701258869573205000.log
|
||||||
|
m, err := filepath.Glob(logFilePath + "*")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Truef(t, len(m) > 0, "no files were found")
|
||||||
|
|
||||||
|
for _, p := range m {
|
||||||
|
f, err := os.Open(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
fs := bufio.NewScanner(f)
|
||||||
|
fs.Split(bufio.ScanLines)
|
||||||
|
|
||||||
|
for fs.Scan() {
|
||||||
|
s := fs.Text()
|
||||||
|
entry := make(map[string]string)
|
||||||
|
err := json.Unmarshal([]byte(s), &entry)
|
||||||
|
require.NoError(t, err)
|
||||||
|
v, ok := entry["@message"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v == runnerLogMessage {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Truef(t, found, "unable to find consul-template partial message in logs", runnerLogMessage)
|
||||||
|
}
|
||||||
|
|
||||||
// Get a randomly assigned port and then free it again before returning it.
|
// Get a randomly assigned port and then free it again before returning it.
|
||||||
// There is still a race when trying to use it, but should work better
|
// There is still a race when trying to use it, but should work better
|
||||||
// than a static port.
|
// than a static port.
|
||||||
|
|||||||
Reference in New Issue
Block a user