Skip to content
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 a new "vault monitor" command #8477

Merged
merged 63 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
2549fde
Add a new "vault monitor" command
raskchanky Mar 4, 2020
095c161
Fix a failing test and a random compile error
raskchanky Mar 5, 2020
abe7b0b
Add a new function for accessing the log format
raskchanky Mar 5, 2020
700a7e1
Remove unnecessary goroutine in tests
raskchanky Mar 5, 2020
eefda16
Remove the need to send whitespace
raskchanky Mar 5, 2020
c4f0c6b
Refactor the test helper a little
raskchanky Mar 6, 2020
aea83a3
Remove a lock and change the droppedCount to use an atomic
raskchanky Mar 6, 2020
e685d3e
Rename New and new to NewMonitor and newMonitor
raskchanky Mar 6, 2020
ec69fcd
Consolidate goroutines
raskchanky Mar 6, 2020
10c56e7
Remove redundant for loop and change variable name
raskchanky Mar 6, 2020
2527448
go mod vendor
raskchanky Mar 9, 2020
838abcd
Try to fix some failing tests
raskchanky Mar 9, 2020
78f3c0c
whoops, that "go" makes a difference
raskchanky Mar 10, 2020
61ae825
Implement shutdownCh and remove a race condition from tests
raskchanky Mar 11, 2020
60fb41c
Get rid of the grace
raskchanky Mar 11, 2020
f264bb3
trying some more stuff
raskchanky Mar 13, 2020
f8c7805
Little proof of concept for how to replace stopCh with a context, and…
ncabatoff Mar 13, 2020
4375fbb
feedback updates
raskchanky Mar 14, 2020
4c55ab5
Remove the timeouts
raskchanky Mar 16, 2020
1e92efb
Use core.activeContext instead of background
raskchanky Mar 17, 2020
5cabeb8
Attempt to stop a data race that I can't figure out
raskchanky Mar 17, 2020
e03ab93
monitor: fix potential panic of closing a closed channel (#8585)
calvn Mar 18, 2020
cc3fadc
Address more PR feedback
raskchanky Mar 18, 2020
92f2148
Document buf and add a new test for unbuffered channels
raskchanky Mar 18, 2020
eff3405
Remove the timing weirdness
raskchanky Mar 18, 2020
8da04c1
Ensure we're not sending empty log messages
raskchanky Mar 18, 2020
c3a7aa2
Bump timeout to 5 seconds
raskchanky Mar 18, 2020
2a073af
monitor: refactor goroutine in monitor.Start (#8588)
calvn Mar 19, 2020
faaec8b
Remove comment referencing non-existant loop
raskchanky Mar 19, 2020
02f9624
Don't allow a buffer of 0
raskchanky Mar 19, 2020
fa56c60
PR feedback fixes
raskchanky Mar 26, 2020
4067ff3
Go back to using the interface like we should've from the beginning
raskchanky Mar 27, 2020
78092aa
Undo some more stuff I didn't need to change
raskchanky Mar 27, 2020
1e7b56a
Address PR feedback from Brian
raskchanky Apr 1, 2020
85f6f52
Don't need this any more since it got moved into the logical backend
raskchanky Apr 1, 2020
dc5a636
Modify the logger to fix failing tests
raskchanky Apr 1, 2020
ac82aee
No idea why this changed
raskchanky Apr 2, 2020
da2d044
Change the way the debug helper function works
raskchanky Apr 3, 2020
a545a8d
go mod tidy
raskchanky Apr 6, 2020
0f80a2a
more PR feedback
raskchanky Apr 23, 2020
450d3cc
Merge remote-tracking branch 'origin/master' into vault-monitor
jefferai Apr 23, 2020
5ec427c
Reset vendoring to master to take it out of PR
jefferai Apr 23, 2020
596eaaf
Fmt
jefferai Apr 23, 2020
d2a74b3
Remove some accidental vendor files
jefferai Apr 24, 2020
2096a05
Use tagged 0.12.2 release of hclog, not an interim release
jefferai Apr 24, 2020
97bd34f
make log levels case insensitive
raskchanky Apr 24, 2020
5cdf56d
Merge remote-tracking branch 'origin/vault-monitor' into vault-monitor
raskchanky Apr 24, 2020
061c211
Merge remote-tracking branch 'origin/master' into vault-monitor
raskchanky Apr 24, 2020
e11a423
lower case the levels and remove my now useless helper
raskchanky Apr 24, 2020
5c97324
fix the timeout and a compilation error
raskchanky Apr 24, 2020
1182914
remove go-hclog from api
raskchanky Apr 24, 2020
c0483ad
switch to uber atomics and check for already started
raskchanky Apr 24, 2020
7b53da8
Merge remote-tracking branch 'origin/master' into vault-monitor
raskchanky May 1, 2020
c6081b6
add dropped count checking to the client side
raskchanky May 8, 2020
35639e8
Merge remote-tracking branch 'origin/master' into vault-monitor
raskchanky May 11, 2020
8f8d3e8
make some more levels lowercase
raskchanky May 12, 2020
37915a4
Merge remote-tracking branch 'origin/master' into vault-monitor
raskchanky May 13, 2020
2b18c98
go mod vendor
raskchanky May 13, 2020
46d60d2
fix test
raskchanky May 13, 2020
b824167
Merge remote-tracking branch 'origin/master' into vault-monitor
raskchanky May 19, 2020
c9e2138
fix a broken test caused by the configutil refactor
raskchanky May 21, 2020
98bae2e
Merge remote-tracking branch 'origin/master' into vault-monitor
raskchanky May 21, 2020
dd60ee1
go mod vendor again
raskchanky May 21, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
Expand Down Expand Up @@ -65,6 +67,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
Expand All @@ -82,6 +85,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
Expand Down
2 changes: 1 addition & 1 deletion api/sys_audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (c *Sys) DisableAudit(path string) error {
return err
}

// Structures for the requests/resposne are all down here. They aren't
// Structures for the requests/response are all down here. They aren't
// individually documented because the map almost directly to the raw HTTP API
// documentation. Please refer to that documentation for more details.

Expand Down
64 changes: 64 additions & 0 deletions api/sys_monitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package api

import (
"bufio"
"context"
"fmt"
)

// Monitor returns a channel that outputs strings containing the log messages
// coming from the server.
func (c *Sys) Monitor(ctx context.Context, logLevel string) (chan string, error) {
r := c.c.NewRequest("GET", "/v1/sys/monitor")

if logLevel == "" {
r.Params.Add("log_level", "info")
} else {
r.Params.Add("log_level", logLevel)
}

resp, err := c.c.RawRequestWithContext(ctx, r)
if err != nil {
return nil, err
}

logCh := make(chan string, 64)

go func() {
scanner := bufio.NewScanner(resp.Body)
droppedCount := 0

defer close(logCh)
defer resp.Body.Close()

for {
if ctx.Err() != nil {
return
}

if !scanner.Scan() {
return
}

logMessage := scanner.Text()

if droppedCount > 0 {
select {
case logCh <- fmt.Sprintf("Monitor dropped %d logs during monitor request\n", droppedCount):
droppedCount = 0
default:
droppedCount++
continue
}
}

select {
case logCh <- logMessage:
default:
droppedCount++
}
}
}()

return logCh, nil
}
2 changes: 1 addition & 1 deletion builtin/credential/okta/path_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"strings"
"time"

"github.com/hashicorp/go-cleanhttp"
oktaold "github.com/chrismalek/oktasdk-go/okta"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/helper/tokenutil"
"github.com/hashicorp/vault/sdk/logical"
Expand Down
3 changes: 1 addition & 2 deletions command/agent/cf_end_to_end_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"time"

hclog "github.com/hashicorp/go-hclog"
log "github.com/hashicorp/go-hclog"
credCF "github.com/hashicorp/vault-plugin-auth-cf"
"github.com/hashicorp/vault-plugin-auth-cf/testing/certificates"
cfAPI "github.com/hashicorp/vault-plugin-auth-cf/testing/cf"
Expand All @@ -29,7 +28,7 @@ func TestCFEndToEnd(t *testing.T) {
coreConfig := &vault.CoreConfig{
DisableMlock: true,
DisableCache: true,
Logger: log.NewNullLogger(),
Logger: hclog.NewNullLogger(),
CredentialBackends: map[string]logical.Factory{
"cf": credCF.Factory,
},
Expand Down
8 changes: 7 additions & 1 deletion command/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/vault/builtin/logical/ssh"
"github.com/hashicorp/vault/builtin/logical/transit"
"github.com/hashicorp/vault/helper/builtinplugins"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/sdk/physical/inmem"
"github.com/hashicorp/vault/vault"
Expand Down Expand Up @@ -84,11 +85,16 @@ func testVaultServerAllBackends(tb testing.TB) (*api.Client, func()) {
// API client, list of unseal keys (as strings), and a closer function.
func testVaultServerUnseal(tb testing.TB) (*api.Client, []string, func()) {
tb.Helper()
logger := log.NewInterceptLogger(&log.LoggerOptions{
Output: log.DefaultOutput,
Level: log.Debug,
JSONFormat: logging.ParseEnvLogFormat() == logging.JSONFormat,
})

return testVaultServerCoreConfig(tb, &vault.CoreConfig{
DisableMlock: true,
DisableCache: true,
Logger: defaultVaultLogger,
Logger: logger,
CredentialBackends: defaultVaultCredentialBackends,
AuditBackends: defaultVaultAuditBackends,
LogicalBackends: defaultVaultLogicalBackends,
Expand Down
6 changes: 6 additions & 0 deletions command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,12 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
BaseCommand: getBaseCommand(),
}, nil
},
"monitor": func() (cli.Command, error) {
return &MonitorCommand{
BaseCommand: getBaseCommand(),
ShutdownCh: MakeShutdownCh(),
}, nil
},
}
}

Expand Down
118 changes: 118 additions & 0 deletions command/monitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package command

import (
"context"
"fmt"
"strings"

"github.com/hashicorp/vault/sdk/helper/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)

var _ cli.Command = (*MonitorCommand)(nil)
var _ cli.CommandAutocomplete = (*MonitorCommand)(nil)

type MonitorCommand struct {
*BaseCommand

logLevel string

// ShutdownCh is used to capture interrupt signal and end streaming
ShutdownCh chan struct{}
}

func (c *MonitorCommand) Synopsis() string {
return "Stream log messages from a Vault server"
}

func (c *MonitorCommand) Help() string {
helpText := `
Usage: vault monitor [options]

Stream log messages of a Vault server. The monitor command lets you listen
for log levels that may be filtered out of the server logs. For example,
the server may be logging at the INFO level, but with the monitor command
you can set -log-level=DEBUG.

` + c.Flags().Help()

return strings.TrimSpace(helpText)
}

func (c *MonitorCommand) Flags() *FlagSets {
set := c.flagSet(FlagSetHTTP)

f := set.NewFlagSet("Monitor Options")
f.StringVar(&StringVar{
Name: "log-level",
Target: &c.logLevel,
Default: "info",
Completion: complete.PredictSet("trace", "debug", "info", "warn", "error"),
Usage: "If passed, the log level to monitor logs. Supported values" +
"(in order of detail) are \"trace\", \"debug\", \"info\", \"warn\"" +
" and \"error\". These are not case sensitive.",
})

return set
}

func (c *MonitorCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictNothing
}

func (c *MonitorCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}

func (c *MonitorCommand) Run(args []string) int {
f := c.Flags()

if err := f.Parse(args); err != nil {
c.UI.Error(err.Error())
return 1
}

parsedArgs := f.Args()
if len(parsedArgs) > 0 {
c.UI.Error(fmt.Sprintf("Too many arguments (expected 0, got %d)", len(parsedArgs)))
return 1
}

c.logLevel = strings.ToLower(c.logLevel)
validLevels := []string{"trace", "debug", "info", "warn", "error"}
if !strutil.StrListContains(validLevels, c.logLevel) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to update this so we do not care about case. Elsewhere were allow trace to be specified.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah generally we use lowercase not uppercase for log levels in commands, so if we're going to print valid levels out we should print out lowercase levels, not uppercase.

c.UI.Error(fmt.Sprintf("%s is an unknown log level. Valid log levels are: %s", c.logLevel, validLevels))
return 1
}

client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
return 2
}

// Remove the default 60 second timeout so we can stream indefinitely
client.SetClientTimeout(0)

var logCh chan string
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
logCh, err = client.Sys().Monitor(ctx, c.logLevel)
if err != nil {
c.UI.Error(fmt.Sprintf("Error starting monitor: %s", err))
return 1
}

for {
select {
case log, ok := <-logCh:
if !ok {
return 0
}
c.UI.Info(log)
case <-c.ShutdownCh:
return 0
}
}
}
99 changes: 99 additions & 0 deletions command/monitor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package command

import (
"strings"
"sync/atomic"
"testing"
"time"

"github.com/hashicorp/vault/helper/testhelpers"
"github.com/mitchellh/cli"
)

func testMonitorCommand(tb testing.TB) (*cli.MockUi, *MonitorCommand) {
tb.Helper()

ui := cli.NewMockUi()
return ui, &MonitorCommand{
BaseCommand: &BaseCommand{
UI: ui,
},
}
}

func TestMonitorCommand_Run(t *testing.T) {
t.Parallel()

cases := []struct {
name string
args []string
out string
code int64
}{
{
"valid",
[]string{
"-log-level=debug",
},
"",
0,
},
{
"too_many_args",
[]string{
"-log-level=debug",
"foo",
},
"Too many arguments",
1,
},
{
"unknown_log_level",
[]string{
"-log-level=haha",
},
"haha is an unknown log level",
1,
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()
client, closer := testVaultServer(t)
defer closer()

var code int64
shutdownCh := make(chan struct{})

ui, cmd := testMonitorCommand(t)
cmd.client = client
cmd.ShutdownCh = shutdownCh

stopCh := testhelpers.GenerateDebugLogs(t, client)

go func() {
atomic.StoreInt64(&code, int64(cmd.Run(tc.args)))
}()

select {
case <-time.After(3 * time.Second):
stopCh <- struct{}{}
close(shutdownCh)
}

if atomic.LoadInt64(&code) != tc.code {
t.Errorf("expected %d to be %d", code, tc.code)
}

combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
if !strings.Contains(combined, tc.out) {
t.Fatalf("expected %q to contain %q", combined, tc.out)
}

<-stopCh
})
}
}
Loading