Add a new "vault monitor" command (#8477)

Add a new "vault monitor" command

Co-authored-by: ncabatoff <ncabatoff@hashicorp.com>
Co-authored-by: Calvin Leung Huang <cleung2010@gmail.com>
Co-authored-by: Jeff Mitchell <jeffrey.mitchell@gmail.com>
This commit is contained in:
Josh Black
2020-05-21 13:07:50 -07:00
committed by GitHub
parent 399eb357f4
commit af5338b485
95 changed files with 1957 additions and 445 deletions

View File

@@ -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=
@@ -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=
@@ -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=

View File

@@ -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.

64
api/sys_monitor.go Normal file
View File

@@ -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
}

View File

@@ -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"

View File

@@ -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"
@@ -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,
},

View File

@@ -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"
@@ -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,

View File

@@ -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
},
}
}

118
command/monitor.go Normal file
View File

@@ -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) {
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
command/monitor_test.go Normal file
View File

@@ -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
})
}
}

View File

@@ -412,7 +412,7 @@ func (c *ServerCommand) runRecoveryMode() int {
return 1
}
c.logger = log.New(&log.LoggerOptions{
c.logger = log.NewInterceptLogger(&log.LoggerOptions{
Output: c.gatedWriter,
Level: level,
// Note that if logFormat is either unspecified or standard, then
@@ -868,14 +868,16 @@ func (c *ServerCommand) Run(args []string) int {
return 1
}
config.LogFormat = logFormat.String()
if c.flagDevThreeNode || c.flagDevFourCluster {
c.logger = log.New(&log.LoggerOptions{
c.logger = log.NewInterceptLogger(&log.LoggerOptions{
Mutex: &sync.Mutex{},
Output: c.gatedWriter,
Level: log.Trace,
})
} else {
c.logger = log.New(&log.LoggerOptions{
c.logger = log.NewInterceptLogger(&log.LoggerOptions{
Output: c.gatedWriter,
Level: level,
// Note that if logFormat is either unspecified or standard, then

2
go.mod
View File

@@ -101,7 +101,7 @@ require (
github.com/kr/pretty v0.2.0
github.com/kr/text v0.1.0
github.com/lib/pq v1.2.0
github.com/mattn/go-colorable v0.1.4
github.com/mattn/go-colorable v0.1.6
github.com/mholt/archiver v3.1.1+incompatible
github.com/michaelklishin/rabbit-hole v0.0.0-20191008194146-93d9988f0cd5
github.com/mitchellh/cli v1.0.0

17
go.sum
View File

@@ -191,9 +191,12 @@ github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
@@ -234,7 +237,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@@ -294,6 +296,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -406,6 +409,7 @@ github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.13.0 h1:Do32YnDMnq7v7FU50AgH+1ExKCOkl9HBxvSI1JWr+rA=
github.com/hashicorp/go-hclog v0.13.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -494,8 +498,6 @@ github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1 h1:fA6cFH8lIPH2M4
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1/go.mod h1:MP3kfr0N+7miOTZFwKv952b9VkXM4S2Q6YtQCiNKWq8=
github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e h1:0GK1BNBfglD2sydZ4XXMjJElhY8bC2TDdc0vk1Q9zbA=
github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e/go.mod h1:SCsKcChP8yrtOHXOeTD7oRk0oflj3IxA9y9zTOGtQ8s=
github.com/hashicorp/vault-plugin-secrets-ad v0.6.5 h1:wrHzXSD6qmKvkuHaQn+BNj89+HGhMNchxAckGnd7YTc=
github.com/hashicorp/vault-plugin-secrets-ad v0.6.5/go.mod h1:kk98nB+cwDbt3I7UGQq3ota7+eHZrGSTQZfSRGpluvA=
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5 h1:BOOtSls+BQ1EtPmpE9LoqZztsEZ1fRWVSkHWtRIrCB4=
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5/go.mod h1:gAoReoUpBHaBwkxQqTK7FY8nQC0MuaZHLiW5WOSny5g=
github.com/hashicorp/vault-plugin-secrets-azure v0.5.6 h1:4PgQ5rCT29wW5PMyebEhPkEYuR5s+SnInuZz3x2cP50=
@@ -589,12 +591,12 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11 h1:YFh+sjyJTMQSYjKwM4dFKhJPJC/wfo98tPUc17HdoYw=
github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:Ah2dBMoxZEqk118as2T4u4fjfXarE0pPnMJaArZQZsI=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
@@ -978,15 +980,20 @@ golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

166
helper/monitor/monitor.go Normal file
View File

@@ -0,0 +1,166 @@
package monitor
import (
"fmt"
"time"
log "github.com/hashicorp/go-hclog"
"go.uber.org/atomic"
)
// Monitor provides a mechanism to stream logs using go-hclog
// InterceptLogger and SinkAdapter. It allows streaming of logs
// at a different log level than what is set on the logger.
type Monitor interface {
// Start returns a channel of log messages which are sent
// every time a log message occurs
Start() <-chan []byte
// Stop de-registers the sink from the InterceptLogger
// and closes the log channels
Stop()
}
// monitor implements the Monitor interface. Note that this
// struct is not threadsafe.
type monitor struct {
sink log.SinkAdapter
// logger is the logger we will be monitoring
logger log.InterceptLogger
// logCh is a buffered chan where we send logs when streaming
logCh chan []byte
// doneCh coordinates the shutdown of logCh
doneCh chan struct{}
// droppedCount is the current count of messages
// that were dropped from the logCh buffer.
droppedCount *atomic.Uint32
bufSize int
// dropCheckInterval is the amount of time we should
// wait to check for dropped messages. Defaults
// to 3 seconds
dropCheckInterval time.Duration
// started is whether the monitor has been started or not.
// This is to ensure that we don't start it again until
// it has been shut down.
started *atomic.Bool
}
// NewMonitor creates a new Monitor. Start must be called in order to actually start
// streaming logs. buf is the buffer size of the channel that sends log messages.
func NewMonitor(buf int, logger log.InterceptLogger, opts *log.LoggerOptions) (Monitor, error) {
return newMonitor(buf, logger, opts)
}
func newMonitor(buf int, logger log.InterceptLogger, opts *log.LoggerOptions) (*monitor, error) {
if buf <= 0 {
return nil, fmt.Errorf("buf must be greater than zero")
}
sw := &monitor{
logger: logger,
logCh: make(chan []byte, buf),
doneCh: make(chan struct{}),
bufSize: buf,
dropCheckInterval: 3 * time.Second,
droppedCount: atomic.NewUint32(0),
started: atomic.NewBool(false),
}
opts.Output = sw
sink := log.NewSinkAdapter(opts)
sw.sink = sink
return sw, nil
}
// Stop deregisters the sink and stops the monitoring process
func (d *monitor) Stop() {
d.logger.DeregisterSink(d.sink)
close(d.doneCh)
d.started.Store(false)
}
// Start registers a sink on the monitor's logger and starts sending
// received log messages over the returned channel.
func (d *monitor) Start() <-chan []byte {
// Check to see if this has already been started. If not, flag
// it and proceed. If so, bail out early.
if !d.started.CAS(false, true) {
return nil
}
// register our sink with the logger
d.logger.RegisterSink(d.sink)
streamCh := make(chan []byte, d.bufSize)
// Run a go routine that listens for streamed
// log messages and sends them to streamCh.
//
// It also periodically checks for dropped
// messages and makes room on the logCh to add
// a dropped message count warning
go func() {
defer close(streamCh)
ticker := time.NewTicker(d.dropCheckInterval)
defer ticker.Stop()
var logMessage []byte
for {
logMessage = nil
select {
case <-ticker.C:
// Check if there have been any dropped messages.
dc := d.droppedCount.Load()
if dc > 0 {
logMessage = []byte(fmt.Sprintf("Monitor dropped %d logs during monitor request\n", dc))
d.droppedCount.Swap(0)
}
case logMessage = <-d.logCh:
case <-d.doneCh:
return
}
if len(logMessage) > 0 {
select {
case <-d.doneCh:
return
case streamCh <- logMessage:
}
}
}
}()
return streamCh
}
// Write attempts to send latest log to logCh
// it drops the log if channel is unavailable to receive
func (d *monitor) Write(p []byte) (n int, err error) {
// ensure logCh is still open
select {
case <-d.doneCh:
return
default:
}
bytes := make([]byte, len(p))
copy(bytes, p)
select {
case d.logCh <- bytes:
default:
d.droppedCount.Add(1)
}
return len(p), nil
}

View File

@@ -0,0 +1,96 @@
package monitor
import (
"fmt"
"strings"
"testing"
"time"
log "github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/require"
)
func TestMonitor_Start(t *testing.T) {
t.Parallel()
logger := log.NewInterceptLogger(&log.LoggerOptions{
Level: log.Error,
})
m, _ := NewMonitor(512, logger, &log.LoggerOptions{
Level: log.Debug,
})
logCh := m.Start()
defer m.Stop()
go func() {
logger.Debug("test log")
time.Sleep(10 * time.Millisecond)
}()
select {
case l := <-logCh:
require.Contains(t, string(l), "[DEBUG] test log")
return
case <-time.After(5 * time.Second):
t.Fatal("Expected to receive from log channel")
}
}
func TestMonitor_Start_Unbuffered(t *testing.T) {
t.Parallel()
logger := log.NewInterceptLogger(&log.LoggerOptions{
Level: log.Error,
})
_, err := NewMonitor(0, logger, &log.LoggerOptions{
Level: log.Debug,
})
if err == nil {
t.Fatal("expected to get an error, but didn't")
} else {
if !strings.Contains(err.Error(), "greater than zero") {
t.Fatal("expected an error about buf being greater than zero")
}
}
}
// Ensure number of dropped messages are logged
func TestMonitor_DroppedMessages(t *testing.T) {
t.Parallel()
logger := log.NewInterceptLogger(&log.LoggerOptions{
Level: log.Warn,
})
m, _ := newMonitor(5, logger, &log.LoggerOptions{
Level: log.Debug,
})
m.dropCheckInterval = 5 * time.Millisecond
logCh := m.Start()
defer m.Stop()
for i := 0; i <= 100; i++ {
logger.Debug(fmt.Sprintf("test message %d", i))
}
passed := make(chan struct{})
go func() {
for recv := range logCh {
if strings.Contains(string(recv), "Monitor dropped") {
close(passed)
return
}
}
}()
select {
case <-passed:
case <-time.After(2 * time.Second):
require.Fail(t, "expected to see warn dropped messages")
}
}

View File

@@ -493,3 +493,39 @@ func SetRaftAddressProviders(t testing.T, cluster *vault.TestCluster, provider r
core.UnderlyingRawStorage.(*raft.RaftBackend).SetServerAddressProvider(provider)
}
}
func GenerateDebugLogs(t testing.T, client *api.Client) chan struct{} {
t.Helper()
stopCh := make(chan struct{})
ticker := time.NewTicker(time.Second)
var err error
go func() {
for {
select {
case <-stopCh:
ticker.Stop()
stopCh <- struct{}{}
return
case <-ticker.C:
err = client.Sys().Mount("foo", &api.MountInput{
Type: "kv",
Options: map[string]string{
"version": "1",
},
})
if err != nil {
t.Fatal(err)
}
err = client.Sys().Unmount("foo")
if err != nil {
t.Fatal(err)
}
}
}
}()
return stopCh
}

View File

@@ -133,6 +133,7 @@ func Handler(props *vault.HandlerProperties) http.Handler {
mux.Handle("/v1/sys/unseal", handleSysUnseal(core))
mux.Handle("/v1/sys/leader", handleSysLeader(core))
mux.Handle("/v1/sys/health", handleSysHealth(core))
mux.Handle("/v1/sys/monitor", handleLogicalNoForward(core))
mux.Handle("/v1/sys/generate-root/attempt", handleRequestForwarding(core,
handleAuditNonLogical(core, handleSysGenerateRootAttempt(core, vault.GenerateStandardRootTokenStrategy))))
mux.Handle("/v1/sys/generate-root/update", handleRequestForwarding(core,
@@ -274,8 +275,12 @@ func wrapGenericHandler(core *vault.Core, h http.Handler, props *vault.HandlerPr
// Start with the request context
ctx := r.Context()
var cancelFunc context.CancelFunc
// Add our timeout
// Add our timeout, but not for the monitor endpoint, as it's streaming
if strings.HasSuffix(r.URL.Path, "sys/monitor") {
ctx, cancelFunc = context.WithCancel(ctx)
} else {
ctx, cancelFunc = context.WithTimeout(ctx, maxRequestDuration)
}
// Add a size limiter if desired
if maxRequestSize > 0 {
ctx = context.WithValue(ctx, "max_request_size", maxRequestSize)

View File

@@ -86,6 +86,9 @@ func buildLogicalRequestNoAuth(perfStandby bool, w http.ResponseWriter, r *http.
responseWriter = w
case path == "sys/storage/raft/snapshot":
responseWriter = w
case path == "sys/monitor":
passHTTPReq = true
responseWriter = w
}
case "POST", "PUT":
@@ -142,13 +145,13 @@ func buildLogicalRequestNoAuth(perfStandby bool, w http.ResponseWriter, r *http.
return nil, nil, http.StatusMethodNotAllowed, nil
}
request_id, err := uuid.GenerateUUID()
requestId, err := uuid.GenerateUUID()
if err != nil {
return nil, nil, http.StatusBadRequest, errwrap.Wrapf("failed to generate identifier for the request: {{err}}", err)
}
req := &logical.Request{
ID: request_id,
ID: requestId,
Operation: op,
Path: path,
Data: data,

View File

@@ -43,6 +43,7 @@ func fetchStatusCode(r *http.Request, field string) (int, bool, bool) {
func handleSysHealthGet(core *vault.Core, w http.ResponseWriter, r *http.Request) {
code, body, err := getSysHealth(core, r)
if err != nil {
core.Logger().Error("error checking health", "error", err)
respondError(w, code, nil)

82
http/sys_monitor_test.go Normal file
View File

@@ -0,0 +1,82 @@
package http
import (
"context"
"strings"
"testing"
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/testhelpers"
"github.com/hashicorp/vault/sdk/helper/logging"
"github.com/hashicorp/vault/vault"
)
func TestSysMonitorUnknownLogLevel(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{HandlerFunc: Handler})
cluster.Start()
defer cluster.Cleanup()
client := cluster.Cores[0].Client
request := client.NewRequest("GET", "/v1/sys/monitor")
request.Params.Add("log_level", "haha")
_, err := client.RawRequest(request)
if err == nil {
t.Fatal("expected to get an error, but didn't")
} else {
if !strings.Contains(err.Error(), "Code: 400") {
t.Fatalf("expected to receive a 400 error, but got %s instead", err)
}
if !strings.Contains(err.Error(), "unknown log level") {
t.Fatalf("expected to receive a message indicating an unknown log level, but got %s instead", err)
}
}
}
func TestSysMonitorStreamingLogs(t *testing.T) {
logger := log.NewInterceptLogger(&log.LoggerOptions{
Output: log.DefaultOutput,
Level: log.Debug,
JSONFormat: logging.ParseEnvLogFormat() == logging.JSONFormat,
})
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{HandlerFunc: Handler, Logger: logger})
cluster.Start()
defer cluster.Cleanup()
client := cluster.Cores[0].Client
stopCh := testhelpers.GenerateDebugLogs(t, client)
debugCount := 0
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
logCh, err := client.Sys().Monitor(ctx, "DEBUG")
if err != nil {
t.Fatal(err)
}
timeCh := time.After(5 * time.Second)
for {
select {
case log := <-logCh:
if strings.Contains(log, "[DEBUG]") {
debugCount++
}
case <-timeCh:
t.Fatal("Failed to get a DEBUG message after 5 seconds")
}
// If we've seen multiple lines that match what we want,
// it's probably safe to assume streaming is working
if debugCount > 3 {
stopCh <- struct{}{}
break
}
}
<-stopCh
}

View File

@@ -185,13 +185,13 @@ func RespondWithStatusCode(resp *Response, req *Request, code int) (*Response, e
}
// HTTPResponseWriter is optionally added to a request object and can be used to
// write directly to the HTTP response writter.
// write directly to the HTTP response writer.
type HTTPResponseWriter struct {
http.ResponseWriter
written *uint32
}
// NewHTTPResponseWriter creates a new HTTPRepoinseWriter object that wraps the
// NewHTTPResponseWriter creates a new HTTPResponseWriter object that wraps the
// provided io.Writer.
func NewHTTPResponseWriter(w http.ResponseWriter) *HTTPResponseWriter {
return &HTTPResponseWriter{

View File

@@ -742,6 +742,7 @@ func NewCore(conf *CoreConfig) (*Core, error) {
standby: true,
baseLogger: conf.Logger,
logger: conf.Logger.Named("core"),
defaultLeaseTTL: conf.DefaultLeaseTTL,
maxLeaseTTL: conf.MaxLeaseTTL,
cachingDisabled: conf.DisableCache,
@@ -2345,6 +2346,12 @@ func (c *Core) SanitizedConfig() map[string]interface{} {
return conf.(*server.Config).Sanitized()
}
// LogFormat returns the log format current in use.
func (c *Core) LogFormat() string {
conf := c.rawConfig.Load()
return conf.(*server.Config).LogFormat
}
// MetricsHelper returns the global metrics helper which allows external
// packages to access Vault's internal metrics.
func (c *Core) MetricsHelper() *metricsutil.MetricsHelper {

View File

@@ -24,6 +24,7 @@ import (
func TestKVv2_UpgradePaths(t *testing.T) {
m := new(sync.Mutex)
logOut := new(bytes.Buffer)
logger := hclog.New(&hclog.LoggerOptions{
Output: logOut,
Mutex: m,

View File

@@ -14,7 +14,6 @@ import (
// https://github.com/hashicorp/vault/pull/6920
func TestRecoverFromPanic(t *testing.T) {
logger := hclog.New(nil)
coreConfig := &vault.CoreConfig{
LogicalBackends: map[string]logical.Factory{
"noop": vault.NoopBackendFactory,

View File

@@ -21,11 +21,12 @@ import (
"github.com/hashicorp/errwrap"
log "github.com/hashicorp/go-hclog"
memdb "github.com/hashicorp/go-memdb"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-multierror"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/helper/hostutil"
"github.com/hashicorp/vault/helper/identity"
"github.com/hashicorp/vault/helper/metricsutil"
"github.com/hashicorp/vault/helper/monitor"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/physical/raft"
"github.com/hashicorp/vault/sdk/framework"
@@ -157,6 +158,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend {
b.Backend.Paths = append(b.Backend.Paths, b.pprofPaths()...)
b.Backend.Paths = append(b.Backend.Paths, b.remountPath())
b.Backend.Paths = append(b.Backend.Paths, b.metricsPath())
b.Backend.Paths = append(b.Backend.Paths, b.monitorPath())
b.Backend.Paths = append(b.Backend.Paths, b.hostInfoPath())
if core.rawEnabled {
@@ -2514,6 +2516,72 @@ func (b *SystemBackend) handleMetrics(ctx context.Context, req *logical.Request,
return b.Core.metricsHelper.ResponseForFormat(format), nil
}
func (b *SystemBackend) handleMonitor(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
ll := data.Get("log_level").(string)
w := req.ResponseWriter
resp := &logical.Response{}
if ll == "" {
ll = "info"
}
logLevel := log.LevelFromString(ll)
if logLevel == log.NoLevel {
return logical.ErrorResponse("unknown log level"), nil
}
flusher, ok := w.ResponseWriter.(http.Flusher)
if !ok {
return logical.ErrorResponse("streaming not supported"), nil
}
isJson := b.Core.LogFormat() == "json"
logger := b.Core.Logger().(log.InterceptLogger)
mon, err := monitor.NewMonitor(512, logger, &log.LoggerOptions{
Level: logLevel,
JSONFormat: isJson,
})
defer mon.Stop()
if err != nil {
return resp, err
}
logCh := mon.Start()
if logCh == nil {
return resp, fmt.Errorf("error trying to start a monitor that's already been started")
}
w.WriteHeader(http.StatusOK)
// 0 byte write is needed before the Flush call so that if we are using
// a gzip stream it will go ahead and write out the HTTP response header
_, err = w.Write([]byte(""))
if err != nil {
return resp, fmt.Errorf("error seeding flusher: %w", err)
}
flusher.Flush()
// Stream logs until the connection is closed.
for {
select {
case <-ctx.Done():
return resp, nil
case l := <-logCh:
_, err = fmt.Fprint(w, string(l))
if err != nil {
return resp, err
}
flusher.Flush()
}
}
}
// handleHostInfo collects and returns host-related information, which includes
// system information, cpu, disk, and memory usage. Any capture-related errors
// returned by the collection method will be returned as response warnings.

View File

@@ -1199,6 +1199,25 @@ func (b *SystemBackend) metricsPath() *framework.Path {
}
func (b *SystemBackend) monitorPath() *framework.Path {
return &framework.Path{
Pattern: "monitor",
Fields: map[string]*framework.FieldSchema{
"log_level": &framework.FieldSchema{
Type: framework.TypeString,
Description: "Log level to view system logs at. Currently supported values are \"trace\", \"debug\", \"info\", \"warn\", \"error\".",
Query: true,
},
},
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.ReadOperation: b.handleMonitor,
},
HelpSynopsis: strings.TrimSpace(sysHelp["monitor"][0]),
HelpDescription: strings.TrimSpace(sysHelp["monitor"][1]),
}
}
func (b *SystemBackend) hostInfoPath() *framework.Path {
return &framework.Path{
Pattern: "host-info/?",

View File

@@ -14,6 +14,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"github.com/hashicorp/vault/internalshared/configutil"
"io"
"io/ioutil"
"math/big"
@@ -1493,7 +1494,9 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te
}
if coreConfig.RawConfig == nil {
coreConfig.RawConfig = new(server.Config)
c := new(server.Config)
c.SharedConfig = &configutil.SharedConfig{LogFormat: logging.UnspecifiedFormat.String()}
coreConfig.RawConfig = c
}
addAuditBackend := len(coreConfig.AuditBackends) == 0

View File

@@ -1,5 +0,0 @@
language: go
go:
- 1.8.x
- tip

View File

@@ -1,27 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,30 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/mattn/go-colorable"
version = "0.0.9"
[[constraint]]
name = "github.com/mattn/go-isatty"
version = "0.0.3"

View File

@@ -1,6 +1,12 @@
# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) [![Build Status](https://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
# Archived project. No maintenance.
This project is not maintained anymore and is archived. Feel free to fork and
make your own changes if needed. For more detail read my blog post: [Taking an indefinite sabbatical from my projects](https://arslan.io/2018/10/09/taking-an-indefinite-sabbatical-from-my-projects/)
Thanks to everyone for their valuable feedback and contributions.
# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color)
Color lets you use colorized outputs in terms of [ANSI Escape
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
@@ -17,9 +23,6 @@ suits you.
go get github.com/fatih/color
```
Note that the `vendor` folder is here for stability. Remove the folder if you
already have the dependencies in your GOPATH.
## Examples
### Standard colors

8
vendor/github.com/fatih/color/go.mod generated vendored Normal file
View File

@@ -0,0 +1,8 @@
module github.com/fatih/color
go 1.13
require (
github.com/mattn/go-colorable v0.1.4
github.com/mattn/go-isatty v0.0.11
)

8
vendor/github.com/fatih/color/go.sum generated vendored Normal file
View File

@@ -0,0 +1,8 @@
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -290,14 +290,14 @@ func (c *Child) start() error {
// down the exit channel.
c.stopLock.RLock()
defer c.stopLock.RUnlock()
if c.stopped {
return
}
if !c.stopped {
select {
case <-c.stopCh:
case exitCh <- code:
}
}
close(exitCh)
}()
c.exitCh = exitCh
@@ -365,16 +365,13 @@ func (c *Child) reload() error {
return c.signal(c.reloadSignal)
}
// kill sends the signal to kill the process using the configured signal
// if set, else the default system signal
func (c *Child) kill(immediately bool) {
if !c.running() {
return
}
exited := false
process := c.cmd.Process
if c.cmd.ProcessState != nil {
log.Printf("[DEBUG] (child) Kill() called but process dead; not waiting for splay.")
return
} else if immediately {
log.Printf("[DEBUG] (child) Kill() called but performing immediate shutdown; not waiting for splay.")
} else {
@@ -384,6 +381,9 @@ func (c *Child) kill(immediately bool) {
}
}
exited := false
process := c.cmd.Process
if c.killSignal != nil {
if err := process.Signal(c.killSignal); err == nil {
// Wait a few seconds for it to exit
@@ -410,6 +410,11 @@ func (c *Child) kill(immediately bool) {
}
func (c *Child) running() bool {
select {
case <-c.exitCh:
return false
default:
}
return c.cmd != nil && c.cmd.Process != nil
}

View File

@@ -33,6 +33,9 @@ const (
// DefaultKillSignal is the default signal for termination.
DefaultKillSignal = syscall.SIGINT
// DefaultBlockQueryWaitTime is amount of time in seconds to do a blocking query for
DefaultBlockQueryWaitTime = 60 * time.Second
)
var (
@@ -48,6 +51,9 @@ type Config struct {
// Dedup is used to configure the dedup settings
Dedup *DedupConfig `mapstructure:"deduplicate"`
// DefaultDelims is used to configure the default delimiters for templates
DefaultDelims *DefaultDelims `mapstructure:"default_delimiters"`
// Exec is the configuration for exec/supervise mode.
Exec *ExecConfig `mapstructure:"exec"`
@@ -84,6 +90,9 @@ type Config struct {
// Additional command line options
// Run once, executing each template exactly once, and exit
Once bool
// BlockQueryWaitTime is amount of time in seconds to do a blocking query for
BlockQueryWaitTime *time.Duration `mapstructure:"block_query_wait"`
}
// Copy returns a deep copy of the current configuration. This is useful because
@@ -104,6 +113,10 @@ func (c *Config) Copy() *Config {
o.Dedup = c.Dedup.Copy()
}
if c.DefaultDelims != nil {
o.DefaultDelims = c.DefaultDelims.Copy()
}
if c.Exec != nil {
o.Exec = c.Exec.Copy()
}
@@ -136,6 +149,8 @@ func (c *Config) Copy() *Config {
o.Once = c.Once
o.BlockQueryWaitTime = c.BlockQueryWaitTime
return &o
}
@@ -163,6 +178,10 @@ func (c *Config) Merge(o *Config) *Config {
r.Dedup = r.Dedup.Merge(o.Dedup)
}
if o.DefaultDelims != nil {
r.DefaultDelims = r.DefaultDelims.Merge(o.DefaultDelims)
}
if o.Exec != nil {
r.Exec = r.Exec.Merge(o.Exec)
}
@@ -205,6 +224,8 @@ func (c *Config) Merge(o *Config) *Config {
r.Once = o.Once
r.BlockQueryWaitTime = o.BlockQueryWaitTime
return r
}
@@ -229,6 +250,7 @@ func Parse(s string) (*Config, error) {
"consul.ssl",
"consul.transport",
"deduplicate",
"default_delimiters",
"env",
"exec",
"exec.env",
@@ -383,6 +405,7 @@ func (c *Config) GoString() string {
return fmt.Sprintf("&Config{"+
"Consul:%#v, "+
"Dedup:%#v, "+
"DefaultDelims:%#v, "+
"Exec:%#v, "+
"KillSignal:%s, "+
"LogLevel:%s, "+
@@ -394,9 +417,11 @@ func (c *Config) GoString() string {
"Vault:%#v, "+
"Wait:%#v,"+
"Once:%#v"+
"BlockQueryWaitTime:%#v"+
"}",
c.Consul,
c.Dedup,
c.DefaultDelims,
c.Exec,
SignalGoString(c.KillSignal),
StringGoString(c.LogLevel),
@@ -408,6 +433,7 @@ func (c *Config) GoString() string {
c.Vault,
c.Wait,
c.Once,
TimeDurationGoString(c.BlockQueryWaitTime),
)
}
@@ -438,6 +464,7 @@ func DefaultConfig() *Config {
return &Config{
Consul: DefaultConsulConfig(),
Dedup: DefaultDedupConfig(),
DefaultDelims: DefaultDefaultDelims(),
Exec: DefaultExecConfig(),
Syslog: DefaultSyslogConfig(),
Templates: DefaultTemplateConfigs(),
@@ -465,6 +492,10 @@ func (c *Config) Finalize() {
}
c.Dedup.Finalize()
if c.DefaultDelims == nil {
c.DefaultDelims = DefaultDefaultDelims()
}
if c.Exec == nil {
c.Exec = DefaultExecConfig()
}
@@ -517,6 +548,11 @@ func (c *Config) Finalize() {
if c.Once {
c.Wait = &WaitConfig{Enabled: Bool(false)}
}
// defaults WaitTime to 60 seconds
if c.BlockQueryWaitTime == nil {
c.BlockQueryWaitTime = TimeDuration(DefaultBlockQueryWaitTime)
}
}
func stringFromEnv(list []string, def string) *string {

View File

@@ -8,6 +8,10 @@ type ConsulConfig struct {
// Address is the address of the Consul server. It may be an IP or FQDN.
Address *string
// Namespace is the Consul namespace to use for reading/writing. This can
// also be set via the CONSUL_NAMESPACE environment variable.
Namespace *string `mapstructure:"namespace"`
// Auth is the HTTP basic authentication for communicating with Consul.
Auth *AuthConfig `mapstructure:"auth"`
@@ -46,6 +50,8 @@ func (c *ConsulConfig) Copy() *ConsulConfig {
o.Address = c.Address
o.Namespace = c.Namespace
if c.Auth != nil {
o.Auth = c.Auth.Copy()
}
@@ -89,6 +95,10 @@ func (c *ConsulConfig) Merge(o *ConsulConfig) *ConsulConfig {
r.Address = o.Address
}
if o.Namespace != nil {
r.Namespace = o.Namespace
}
if o.Auth != nil {
r.Auth = r.Auth.Merge(o.Auth)
}
@@ -120,6 +130,10 @@ func (c *ConsulConfig) Finalize() {
}, "")
}
if c.Namespace == nil {
c.Namespace = stringFromEnv([]string{"CONSUL_NAMESPACE"}, "")
}
if c.Auth == nil {
c.Auth = DefaultAuthConfig()
}
@@ -156,6 +170,7 @@ func (c *ConsulConfig) GoString() string {
return fmt.Sprintf("&ConsulConfig{"+
"Address:%s, "+
"Namespace:%s, "+
"Auth:%#v, "+
"Retry:%#v, "+
"SSL:%#v, "+
@@ -163,6 +178,7 @@ func (c *ConsulConfig) GoString() string {
"Transport:%#v"+
"}",
StringGoString(c.Address),
StringGoString(c.Namespace),
c.Auth,
c.Retry,
c.SSL,

View File

@@ -15,6 +15,9 @@ const (
// DefaultDedupMaxStale is the default max staleness for the deduplication
// manager.
DefaultDedupMaxStale = DefaultMaxStale
// DefaultDedupBlockQueryWaitTime is the default amount of time to do a blocking query for the deduplication
DefaultDedupBlockQueryWaitTime = 60 * time.Second
)
// DedupConfig is used to enable the de-duplication mode, which depends
@@ -32,6 +35,9 @@ type DedupConfig struct {
// TTL is the Session TTL used for lock acquisition, defaults to 15 seconds.
TTL *time.Duration `mapstructure:"ttl"`
// BlockQueryWaitTime is amount of time to do a blocking query for, defaults to 60 seconds.
BlockQueryWaitTime *time.Duration `mapstructure:"block_query_wait"`
}
// DefaultDedupConfig returns a configuration that is populated with the
@@ -51,6 +57,7 @@ func (c *DedupConfig) Copy() *DedupConfig {
o.MaxStale = c.MaxStale
o.Prefix = c.Prefix
o.TTL = c.TTL
o.BlockQueryWaitTime = c.BlockQueryWaitTime
return &o
}
@@ -88,6 +95,10 @@ func (c *DedupConfig) Merge(o *DedupConfig) *DedupConfig {
r.TTL = o.TTL
}
if o.BlockQueryWaitTime != nil {
r.BlockQueryWaitTime = o.BlockQueryWaitTime
}
return r
}
@@ -97,7 +108,8 @@ func (c *DedupConfig) Finalize() {
c.Enabled = Bool(false ||
TimeDurationPresent(c.MaxStale) ||
StringPresent(c.Prefix) ||
TimeDurationPresent(c.TTL))
TimeDurationPresent(c.TTL) ||
TimeDurationPresent(c.BlockQueryWaitTime))
}
if c.MaxStale == nil {
@@ -111,6 +123,10 @@ func (c *DedupConfig) Finalize() {
if c.TTL == nil {
c.TTL = TimeDuration(DefaultDedupTTL)
}
if c.BlockQueryWaitTime == nil {
c.BlockQueryWaitTime = TimeDuration(DefaultDedupBlockQueryWaitTime)
}
}
// GoString defines the printable version of this struct.
@@ -122,11 +138,13 @@ func (c *DedupConfig) GoString() string {
"Enabled:%s, "+
"MaxStale:%s, "+
"Prefix:%s, "+
"TTL:%s"+
"TTL:%s, "+
"BlockQueryWaitTime:%s"+
"}",
BoolGoString(c.Enabled),
TimeDurationGoString(c.MaxStale),
StringGoString(c.Prefix),
TimeDurationGoString(c.TTL),
TimeDurationGoString(c.BlockQueryWaitTime),
)
}

View File

@@ -0,0 +1,53 @@
package config
// DefaultDelims is used to configure the default delimiters used for all templates
type DefaultDelims struct {
// Left is the left delimiter for templating
Left *string `mapstructure:"left"`
// Right is the right delimiter for templating
Right *string `mapstructure:"right"`
}
// DefaultDefaultDelims returns the default DefaultDelims
func DefaultDefaultDelims() *DefaultDelims {
return &DefaultDelims{}
}
// Copy returns a copy of the DefaultDelims
func (c *DefaultDelims) Copy() *DefaultDelims {
if c == nil {
return nil
}
return &DefaultDelims{
Left: c.Left,
Right: c.Right,
}
}
// Merge merges the DefaultDelims
func (c *DefaultDelims) Merge(o *DefaultDelims) *DefaultDelims {
if c == nil {
if o == nil {
return nil
}
return o.Copy()
}
if o == nil {
return c.Copy()
}
r := c.Copy()
if o.Left != nil {
r.Left = o.Left
}
if o.Right != nil {
r.Right = o.Right
}
return r
}

View File

@@ -40,8 +40,7 @@ type ExecConfig struct {
// EnvConfig is the environmental customizations.
Env *EnvConfig `mapstructure:"env"`
// KillSignal is the signal to send to the command to kill it gracefully. The
// default value is "SIGTERM".
// KillSignal is the signal to send to the command to kill it gracefully.
KillSignal *os.Signal `mapstructure:"kill_signal"`
// KillTimeout is the amount of time to give the process to cleanup before

View File

@@ -1,16 +1,26 @@
package config
import "fmt"
import (
"fmt"
"github.com/hashicorp/consul-template/version"
)
const (
// DefaultSyslogFacility is the default facility to log to.
DefaultSyslogFacility = "LOCAL0"
)
var (
// DefaultSyslogName is the default app name in syslog.
DefaultSyslogName = version.Name
)
// SyslogConfig is the configuration for syslog.
type SyslogConfig struct {
Enabled *bool `mapstructure:"enabled"`
Facility *string `mapstructure:"facility"`
Name *string `mapstructure:"name"`
}
// DefaultSyslogConfig returns a configuration that is populated with the
@@ -28,6 +38,7 @@ func (c *SyslogConfig) Copy() *SyslogConfig {
var o SyslogConfig
o.Enabled = c.Enabled
o.Facility = c.Facility
o.Name = c.Name
return &o
}
@@ -57,18 +68,26 @@ func (c *SyslogConfig) Merge(o *SyslogConfig) *SyslogConfig {
r.Facility = o.Facility
}
if o.Name != nil {
r.Name = o.Name
}
return r
}
// Finalize ensures there no nil pointers.
func (c *SyslogConfig) Finalize() {
if c.Enabled == nil {
c.Enabled = Bool(StringPresent(c.Facility))
c.Enabled = Bool(StringPresent(c.Facility) || StringPresent(c.Name))
}
if c.Facility == nil {
c.Facility = String(DefaultSyslogFacility)
}
if c.Name == nil {
c.Name = String(DefaultSyslogName)
}
}
// GoString defines the printable version of this struct.
@@ -80,8 +99,10 @@ func (c *SyslogConfig) GoString() string {
return fmt.Sprintf("&SyslogConfig{"+
"Enabled:%s, "+
"Facility:%s"+
"Name:%s"+
"}",
BoolGoString(c.Enabled),
StringGoString(c.Facility),
StringGoString(c.Name),
)
}

View File

@@ -12,11 +12,6 @@ const (
// vault to version 1.1.0 or newer.
EnvVaultSkipVerify = "VAULT_SKIP_VERIFY"
// DefaultVaultGrace is the default grace period before which to read a new
// secret from Vault. If a lease is due to expire in 15 seconds, Consul
// Template will read a new secret at that time minus this value.
DefaultVaultGrace = 15 * time.Second
// DefaultVaultRenewToken is the default value for if the Vault token should
// be renewed.
DefaultVaultRenewToken = true
@@ -42,10 +37,6 @@ type VaultConfig struct {
// Enabled controls whether the Vault integration is active.
Enabled *bool `mapstructure:"enabled"`
// Grace is the amount of time before a lease is about to expire to force a
// new secret to be read.
Grace *time.Duration `mapstructure:"grace"`
// Namespace is the Vault namespace to use for reading/writing secrets. This can
// also be set via the VAULT_NAMESPACE environment variable.
Namespace *string `mapstructure:"namespace"`
@@ -104,8 +95,6 @@ func (c *VaultConfig) Copy() *VaultConfig {
o.Enabled = c.Enabled
o.Grace = c.Grace
o.Namespace = c.Namespace
o.RenewToken = c.RenewToken
@@ -157,10 +146,6 @@ func (c *VaultConfig) Merge(o *VaultConfig) *VaultConfig {
r.Enabled = o.Enabled
}
if o.Grace != nil {
r.Grace = o.Grace
}
if o.Namespace != nil {
r.Namespace = o.Namespace
}
@@ -204,24 +189,10 @@ func (c *VaultConfig) Finalize() {
}, "")
}
if c.Grace == nil {
c.Grace = TimeDuration(DefaultVaultGrace)
}
if c.Namespace == nil {
c.Namespace = stringFromEnv([]string{"VAULT_NAMESPACE"}, "")
}
if c.RenewToken == nil {
default_renew := DefaultVaultRenewToken
if c.VaultAgentTokenFile != nil {
default_renew = false
}
c.RenewToken = boolFromEnv([]string{
"VAULT_RENEW_TOKEN",
}, default_renew)
}
if c.Retry == nil {
c.Retry = DefaultRetryConfig()
}
@@ -277,6 +248,19 @@ func (c *VaultConfig) Finalize() {
c.Token = stringFromFile([]string{*c.VaultAgentTokenFile}, "")
}
// must be after c.Token setting, as default depends on that.
if c.RenewToken == nil {
default_renew := DefaultVaultRenewToken
if c.VaultAgentTokenFile != nil {
default_renew = false
} else if StringVal(c.Token) == "" {
default_renew = false
}
c.RenewToken = boolFromEnv([]string{
"VAULT_RENEW_TOKEN",
}, default_renew)
}
if c.Transport == nil {
c.Transport = DefaultTransportConfig()
}
@@ -302,7 +286,6 @@ func (c *VaultConfig) GoString() string {
return fmt.Sprintf("&VaultConfig{"+
"Address:%s, "+
"Enabled:%s, "+
"Grace:%s, "+
"Namespace:%s,"+
"RenewToken:%s, "+
"Retry:%#v, "+
@@ -314,7 +297,6 @@ func (c *VaultConfig) GoString() string {
"}",
StringGoString(c.Address),
BoolGoString(c.Enabled),
TimeDurationGoString(c.Grace),
StringGoString(c.Namespace),
BoolGoString(c.RenewToken),
c.Retry,

View File

@@ -38,6 +38,7 @@ type vaultClient struct {
// CreateConsulClientInput is used as input to the CreateConsulClient function.
type CreateConsulClientInput struct {
Address string
Namespace string
Token string
AuthEnabled bool
AuthUsername string
@@ -95,6 +96,10 @@ func (c *ClientSet) CreateConsulClient(i *CreateConsulClientInput) error {
consulConfig.Address = i.Address
}
if i.Namespace != "" {
consulConfig.Namespace = i.Namespace
}
if i.Token != "" {
consulConfig.Token = i.Token
}

View File

@@ -0,0 +1,72 @@
package dependency
import (
"log"
"net/url"
"github.com/pkg/errors"
)
var (
// Ensure implements
_ Dependency = (*ConnectCAQuery)(nil)
)
type ConnectCAQuery struct {
stopCh chan struct{}
}
func NewConnectCAQuery() *ConnectCAQuery {
return &ConnectCAQuery{
stopCh: make(chan struct{}, 1),
}
}
func (d *ConnectCAQuery) Fetch(clients *ClientSet, opts *QueryOptions) (
interface{}, *ResponseMetadata, error,
) {
select {
case <-d.stopCh:
return nil, nil, ErrStopped
default:
}
opts = opts.Merge(nil)
log.Printf("[TRACE] %s: GET %s", d, &url.URL{
Path: "/v1/agent/connect/ca/roots",
RawQuery: opts.String(),
})
certs, md, err := clients.Consul().Agent().ConnectCARoots(
opts.ToConsulOpts())
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
log.Printf("[TRACE] %s: returned %d results", d, len(certs.Roots))
log.Printf("[TRACE] %s: %#v ", d, md)
rm := &ResponseMetadata{
LastIndex: md.LastIndex,
LastContact: md.LastContact,
Block: true,
}
return certs.Roots, rm, nil
}
func (d *ConnectCAQuery) Stop() {
close(d.stopCh)
}
func (d *ConnectCAQuery) CanShare() bool {
return false
}
func (d *ConnectCAQuery) Type() Type {
return TypeConsul
}
func (d *ConnectCAQuery) String() string {
return "connect.caroots"
}

View File

@@ -0,0 +1,77 @@
package dependency
import (
"fmt"
"log"
"net/url"
"github.com/pkg/errors"
)
var (
// Ensure implements
_ Dependency = (*ConnectLeafQuery)(nil)
)
type ConnectLeafQuery struct {
stopCh chan struct{}
service string
}
func NewConnectLeafQuery(service string) *ConnectLeafQuery {
return &ConnectLeafQuery{
stopCh: make(chan struct{}, 1),
service: service,
}
}
func (d *ConnectLeafQuery) Fetch(clients *ClientSet, opts *QueryOptions) (
interface{}, *ResponseMetadata, error,
) {
select {
case <-d.stopCh:
return nil, nil, ErrStopped
default:
}
opts = opts.Merge(nil)
log.Printf("[TRACE] %s: GET %s", d, &url.URL{
Path: "/v1/agent/connect/ca/leaf/" + d.service,
RawQuery: opts.String(),
})
cert, md, err := clients.Consul().Agent().ConnectCALeaf(d.service,
opts.ToConsulOpts())
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
log.Printf("[TRACE] %s: returned response", d)
rm := &ResponseMetadata{
LastIndex: md.LastIndex,
LastContact: md.LastContact,
Block: true,
}
return cert, rm, nil
}
func (d *ConnectLeafQuery) Stop() {
close(d.stopCh)
}
func (d *ConnectLeafQuery) CanShare() bool {
return false
}
func (d *ConnectLeafQuery) Type() Type {
return TypeConsul
}
func (d *ConnectLeafQuery) String() string {
if d.service != "" {
return fmt.Sprintf("connect.caleaf(%s)", d.service)
}
return "connect.caleaf"
}

View File

@@ -51,6 +51,7 @@ type HealthService struct {
Checks api.HealthChecks
Status string
Port int
Weights api.AgentWeights
}
// HealthServiceQuery is the representation of all a service query in Consul.
@@ -62,10 +63,20 @@ type HealthServiceQuery struct {
name string
near string
tag string
connect bool
}
// NewHealthServiceQuery processes the strings to build a service dependency.
func NewHealthServiceQuery(s string) (*HealthServiceQuery, error) {
return healthServiceQuery(s, false)
}
// NewHealthConnect Query processes the strings to build a connect dependency.
func NewHealthConnectQuery(s string) (*HealthServiceQuery, error) {
return healthServiceQuery(s, true)
}
func healthServiceQuery(s string, connect bool) (*HealthServiceQuery, error) {
if !HealthServiceQueryRe.MatchString(s) {
return nil, fmt.Errorf("health.service: invalid format: %q", s)
}
@@ -86,7 +97,8 @@ func NewHealthServiceQuery(s string) (*HealthServiceQuery, error) {
filters = append(filters, f)
case "":
default:
return nil, fmt.Errorf("health.service: invalid filter: %q in %q", f, s)
return nil, fmt.Errorf(
"health.service: invalid filter: %q in %q", f, s)
}
}
sort.Strings(filters)
@@ -101,6 +113,7 @@ func NewHealthServiceQuery(s string) (*HealthServiceQuery, error) {
name: m["name"],
near: m["near"],
tag: m["tag"],
connect: connect,
}, nil
}
@@ -130,10 +143,15 @@ func (d *HealthServiceQuery) Fetch(clients *ClientSet, opts *QueryOptions) (inte
log.Printf("[TRACE] %s: GET %s", d, u)
// Check if a user-supplied filter was given. If so, we may be querying for
// more than healthy services, so we need to implement client-side filtering.
// more than healthy services, so we need to implement client-side
// filtering.
passingOnly := len(d.filters) == 1 && d.filters[0] == HealthPassing
entries, qm, err := clients.Consul().Health().Service(d.name, d.tag, passingOnly, opts.ToConsulOpts())
nodes := clients.Consul().Health().Service
if d.connect {
nodes = clients.Consul().Health().Connect
}
entries, qm, err := nodes(d.name, d.tag, passingOnly, opts.ToConsulOpts())
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
@@ -145,13 +163,14 @@ func (d *HealthServiceQuery) Fetch(clients *ClientSet, opts *QueryOptions) (inte
// Get the status of this service from its checks.
status := entry.Checks.AggregatedStatus()
// If we are not checking only healthy services, filter out services that do
// not match the given filter.
// If we are not checking only healthy services, filter out services
// that do not match the given filter.
if !acceptStatus(d.filters, status) {
continue
}
// Get the address of the service, falling back to the address of the node.
// Get the address of the service, falling back to the address of the
// node.
address := entry.Service.Address
if address == "" {
address = entry.Node.Address
@@ -167,10 +186,12 @@ func (d *HealthServiceQuery) Fetch(clients *ClientSet, opts *QueryOptions) (inte
Address: address,
ID: entry.Service.ID,
Name: entry.Service.Service,
Tags: ServiceTags(deepCopyAndSortTags(entry.Service.Tags)),
Tags: ServiceTags(
deepCopyAndSortTags(entry.Service.Tags)),
Status: status,
Checks: entry.Checks,
Port: entry.Service.Port,
Weights: entry.Service.Weights,
})
}

View File

@@ -8,6 +8,7 @@ import (
"time"
"crypto/x509"
"encoding/json"
"encoding/pem"
"github.com/hashicorp/vault/api"
@@ -141,6 +142,20 @@ func leaseCheckWait(s *Secret) time.Duration {
}
}
// Handle if this is a secret with a rotation period. If this is a rotating secret,
// the rotating secret's TTL will be the duration to sleep before rendering the new secret.
var rotatingSecret bool
if _, ok := s.Data["rotation_period"]; ok && s.LeaseID == "" {
if ttlInterface, ok := s.Data["ttl"]; ok {
if ttlData, err := ttlInterface.(json.Number).Int64(); err == nil {
log.Printf("[DEBUG] Found rotation_period and set lease duration to %d seconds", ttlData)
// Add a second for cushion
base = int(ttlData) + 1
rotatingSecret = true
}
}
}
// Ensure we have a lease duration, since sometimes this can be zero.
if base <= 0 {
base = int(VaultDefaultLeaseDuration.Seconds())
@@ -156,7 +171,9 @@ func leaseCheckWait(s *Secret) time.Duration {
// Use some randomness so many clients do not hit Vault simultaneously.
sleep = sleep * (rand.Float64() + 1) / 2.0
} else {
} else if !rotatingSecret {
// If the secret doesn't have a rotation period, this is a non-renewable leased
// secret.
// For non-renewable leases set the renew duration to use much of the secret
// lease as possible. Use a stagger over 85%-95% of the lease duration so that
// many clients do not hit Vault simultaneously.
@@ -339,8 +356,12 @@ func addPrefixToVKVPath(p, mountPath, apiPrefix string) string {
return path.Join(mountPath, apiPrefix)
default:
p = strings.TrimPrefix(p, mountPath)
// Don't add /data to the path if it's been added manually.
if strings.HasPrefix(p, apiPrefix) {
// Don't add /data/ to the path if it's been added manually.
apiPathPrefix := apiPrefix
if !strings.HasSuffix(apiPrefix, "/") {
apiPathPrefix += "/"
}
if strings.HasPrefix(p, apiPathPrefix) {
return path.Join(mountPath, p)
}
return path.Join(mountPath, apiPrefix, p)

View File

@@ -122,6 +122,9 @@ func (d *VaultReadQuery) Stop() {
// String returns the human-friendly version of this dependency.
func (d *VaultReadQuery) String() string {
if v := d.queryValues["version"]; len(v) > 0 {
return fmt.Sprintf("vault.read(%s.v%s)", d.rawPath, v[0])
}
return fmt.Sprintf("vault.read(%s)", d.rawPath)
}

View File

@@ -1,10 +1,8 @@
package dependency
import (
"log"
"time"
"github.com/hashicorp/vault/api"
"github.com/pkg/errors"
)
var (
@@ -36,7 +34,8 @@ func NewVaultTokenQuery(token string) (*VaultTokenQuery, error) {
}
// Fetch queries the Vault API
func (d *VaultTokenQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
func (d *VaultTokenQuery) Fetch(clients *ClientSet, opts *QueryOptions,
) (interface{}, *ResponseMetadata, error) {
select {
case <-d.stopCh:
return nil, nil, ErrStopped
@@ -44,25 +43,13 @@ func (d *VaultTokenQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interfa
}
if vaultSecretRenewable(d.secret) {
err := renewSecret(clients, d)
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
renewSecret(clients, d)
}
// The secret isn't renewable, probably the generic secret backend.
// TODO This is incorrect when given a non-renewable template. We should
// instead to a lookup self to determine the lease duration.
opts = opts.Merge(&QueryOptions{})
dur := leaseCheckWait(d.secret)
if dur < opts.VaultGrace {
dur = opts.VaultGrace
}
log.Printf("[TRACE] %s: token is not renewable, sleeping for %s", d, dur)
select {
case <-time.After(dur):
case <-d.stopCh:
return nil, nil, ErrStopped
}
return nil, nil, ErrLeaseExpired
}

View File

@@ -16,15 +16,14 @@ var Levels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERR"}
// Config is the configuration for this log setup.
type Config struct {
// Name is the progname as it will appear in syslog output (if enabled).
Name string `json:"name"`
// Level is the log level to use.
Level string `json:"level"`
// Syslog and SyslogFacility are the syslog configuration options.
Syslog bool `json:"syslog"`
SyslogFacility string `json:"syslog_facility"`
// SyslogName is the progname as it will appear in syslog output (if enabled).
SyslogName string `json:"name"`
// Writer is the output where logs should go. If syslog is enabled, data will
// be written to writer in addition to syslog.
@@ -51,7 +50,7 @@ func Setup(config *Config) error {
if config.Syslog {
log.Printf("[DEBUG] (logging) enabling syslog on %s", config.SyslogFacility)
l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, config.Name)
l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, config.SyslogName)
if err != nil {
return fmt.Errorf("error setting up syslog logger: %s", err)
}

View File

@@ -556,23 +556,6 @@ func (r *Runner) Run() error {
}
}
// Check if we need to deliver any rendered signals
if wouldRenderAny || renderedAny {
// Send the signal that a template got rendered
select {
case r.renderedCh <- struct{}{}:
default:
}
}
// Check if we need to deliver any event signals
if newRenderEvent {
select {
case r.renderEventCh <- struct{}{}:
default:
}
}
// Perform the diff and update the known dependencies.
r.diffAndUpdateDeps(runCtx.depsMap)
@@ -601,6 +584,23 @@ func (r *Runner) Run() error {
}
}
// Check if we need to deliver any rendered signals
if wouldRenderAny || renderedAny {
// Send the signal that a template got rendered
select {
case r.renderedCh <- struct{}{}:
default:
}
}
// Check if we need to deliver any event signals
if newRenderEvent {
select {
case r.renderEventCh <- struct{}{}:
default:
}
}
// If we got this far and have a child process, we need to send the reload
// signal to the child process.
if renderedAny && r.child != nil {
@@ -691,12 +691,20 @@ func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (*
// Grab the list of used and missing dependencies.
missing, used := result.Missing, result.Used
if l := missing.Len(); l > 0 {
log.Printf("[DEBUG] (runner) missing data for %d dependencies", l)
for _, missingDependency := range missing.List() {
log.Printf("[DEBUG] (runner) missing dependency: %s", missingDependency)
}
}
// Add the dependency to the list of dependencies for this runner.
for _, d := range used.List() {
// If we've taken over leadership for a template, we may have data
// that is cached, but not have the watcher. We must treat this as
// missing so that we create the watcher and re-run the template.
if isLeader && !r.watcher.Watching(d) {
log.Printf("[DEBUG] (runner) add used dependency %s to missing since isLeader but do not have a watcher", d)
missing.Add(d)
}
if _, ok := runCtx.depsMap[d.String()]; !ok {
@@ -865,12 +873,21 @@ func (r *Runner) init() error {
// config templates is kept so templates can lookup their commands and output
// destinations.
for _, ctmpl := range *r.config.Templates {
leftDelim := config.StringVal(ctmpl.LeftDelim)
if leftDelim == "" {
leftDelim = config.StringVal(r.config.DefaultDelims.Left)
}
rightDelim := config.StringVal(ctmpl.RightDelim)
if rightDelim == "" {
rightDelim = config.StringVal(r.config.DefaultDelims.Right)
}
tmpl, err := template.NewTemplate(&template.NewTemplateInput{
Source: config.StringVal(ctmpl.Source),
Contents: config.StringVal(ctmpl.Contents),
ErrMissingKey: config.BoolVal(ctmpl.ErrMissingKey),
LeftDelim: config.StringVal(ctmpl.LeftDelim),
RightDelim: config.StringVal(ctmpl.RightDelim),
LeftDelim: leftDelim,
RightDelim: rightDelim,
FunctionBlacklist: ctmpl.FunctionBlacklist,
SandboxPath: config.StringVal(ctmpl.SandboxPath),
})
@@ -1232,6 +1249,7 @@ func newClientSet(c *config.Config) (*dep.ClientSet, error) {
if err := clients.CreateConsulClient(&dep.CreateConsulClientInput{
Address: config.StringVal(c.Consul.Address),
Namespace: config.StringVal(c.Consul.Namespace),
Token: config.StringVal(c.Consul.Token),
AuthEnabled: config.BoolVal(c.Consul.Auth.Enabled),
AuthUsername: config.StringVal(c.Consul.Auth.Username),
@@ -1288,6 +1306,7 @@ func newWatcher(c *config.Config, clients *dep.ClientSet, once bool) (*watch.Wat
Clients: clients,
MaxStale: config.TimeDurationVal(c.MaxStale),
Once: c.Once,
BlockQueryWaitTime: config.TimeDurationVal(c.BlockQueryWaitTime),
RenewVault: clients.Vault().Token() != "" && config.BoolVal(c.Vault.RenewToken),
VaultAgentTokenFile: config.StringVal(c.Vault.VaultAgentTokenFile),
RetryFuncConsul: watch.RetryFunc(c.Consul.Retry.RetryFunc()),
@@ -1295,7 +1314,6 @@ func newWatcher(c *config.Config, clients *dep.ClientSet, once bool) (*watch.Wat
// dependencies like reading a file from disk.
RetryFuncDefault: nil,
RetryFuncVault: watch.RetryFunc(c.Vault.Retry.RetryFunc()),
VaultGrace: config.TimeDurationVal(c.Vault.Grace),
VaultToken: clients.Vault().Token(),
})
if err != nil {

View File

@@ -2,7 +2,9 @@ package template
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
@@ -19,6 +21,7 @@ import (
"github.com/BurntSushi/toml"
dep "github.com/hashicorp/consul-template/dependency"
"github.com/hashicorp/consul/api"
socktmpl "github.com/hashicorp/go-sockaddr/template"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
@@ -387,11 +390,9 @@ func byMeta(meta string, services []*dep.HealthService) (groups map[string][]*de
}
getOrDefault := func(m map[string]string, key string) string {
realKey := strings.TrimSuffix(key, "|int")
if val, ok := m[realKey]; ok {
if val != "" {
if val := m[realKey]; val != "" {
return val
}
}
if strings.HasSuffix(key, "|int") {
return "0"
}
@@ -472,6 +473,62 @@ func servicesFunc(b *Brain, used, missing *dep.Set) func(...string) ([]*dep.Cata
}
}
// connectFunc returns or accumulates health connect dependencies.
func connectFunc(b *Brain, used, missing *dep.Set) func(...string) ([]*dep.HealthService, error) {
return func(s ...string) ([]*dep.HealthService, error) {
result := []*dep.HealthService{}
if len(s) == 0 || s[0] == "" {
return result, nil
}
d, err := dep.NewHealthConnectQuery(strings.Join(s, "|"))
if err != nil {
return nil, err
}
used.Add(d)
if value, ok := b.Recall(d); ok {
return value.([]*dep.HealthService), nil
}
missing.Add(d)
return result, nil
}
}
func connectCARootsFunc(b *Brain, used, missing *dep.Set,
) func(...string) ([]*api.CARoot, error) {
return func(...string) ([]*api.CARoot, error) {
d := dep.NewConnectCAQuery()
used.Add(d)
if value, ok := b.Recall(d); ok {
return value.([]*api.CARoot), nil
}
missing.Add(d)
return nil, nil
}
}
func connectLeafFunc(b *Brain, used, missing *dep.Set,
) func(...string) (*api.LeafCert, error) {
return func(s ...string) (*api.LeafCert, error) {
if len(s) == 0 || s[0] == "" {
return nil, nil
}
d := dep.NewConnectLeafQuery(s[0])
used.Add(d)
if value, ok := b.Recall(d); ok {
return value.(*api.LeafCert), nil
}
missing.Add(d)
return nil, nil
}
}
func safeTreeFunc(b *Brain, used, missing *dep.Set) func(string) ([]*dep.KeyPair, error) {
// call treeFunc but explicitly mark that empty data set returned on monitored KV prefix is NOT safe
return treeFunc(b, used, missing, false)
@@ -805,9 +862,11 @@ func loop(ifaces ...interface{}) (<-chan int64, error) {
to64 := func(i interface{}) (int64, error) {
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64:
return int64(v.Int()), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64:
return int64(v.Uint()), nil
case reflect.String:
return parseInt(v.String())
@@ -922,6 +981,19 @@ func parseUint(s string) (uint64, error) {
return result, nil
}
// parseYAML returns a structure for valid YAML
func parseYAML(s string) (interface{}, error) {
if s == "" {
return map[string]interface{}{}, nil
}
var data interface{}
if err := yaml.Unmarshal([]byte(s), &data); err != nil {
return nil, err
}
return data, nil
}
// plugin executes a subprocess as the given command string. It is assumed the
// resulting command returns JSON which is then parsed and returned as the
// value for use in the template.
@@ -1287,6 +1359,148 @@ func modulo(b, a interface{}) (interface{}, error) {
}
}
// minimum returns the minimum between a and b.
func minimum(b, a interface{}) (interface{}, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if av.Int() < bv.Int() {
return av.Int(), nil
}
return bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if av.Int() < int64(bv.Uint()) {
return av.Int(), nil
}
return bv.Uint(), nil
case reflect.Float32, reflect.Float64:
if float64(av.Int()) < bv.Float() {
return av.Int(), nil
}
return bv.Float(), nil
default:
return nil, fmt.Errorf("minimum: unknown type for %q (%T)", bv, b)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if int64(av.Uint()) < bv.Int() {
return av.Uint(), nil
}
return bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if av.Uint() < bv.Uint() {
return av.Uint(), nil
}
return bv.Uint(), nil
case reflect.Float32, reflect.Float64:
if float64(av.Uint()) < bv.Float() {
return av.Uint(), nil
}
return bv.Float(), nil
default:
return nil, fmt.Errorf("minimum: unknown type for %q (%T)", bv, b)
}
case reflect.Float32, reflect.Float64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if av.Float() < float64(bv.Int()) {
return av.Float(), nil
}
return bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if av.Float() < float64(bv.Uint()) {
return av.Float(), nil
}
return bv.Uint(), nil
case reflect.Float32, reflect.Float64:
if av.Float() < bv.Float() {
return av.Float(), nil
}
return bv.Float(), nil
default:
return nil, fmt.Errorf("minimum: unknown type for %q (%T)", bv, b)
}
default:
return nil, fmt.Errorf("minimum: unknown type for %q (%T)", av, a)
}
}
// maximum returns the maximum between a and b.
func maximum(b, a interface{}) (interface{}, error) {
av := reflect.ValueOf(a)
bv := reflect.ValueOf(b)
switch av.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if av.Int() > bv.Int() {
return av.Int(), nil
}
return bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if av.Int() > int64(bv.Uint()) {
return av.Int(), nil
}
return bv.Uint(), nil
case reflect.Float32, reflect.Float64:
if float64(av.Int()) > bv.Float() {
return av.Int(), nil
}
return bv.Float(), nil
default:
return nil, fmt.Errorf("maximum: unknown type for %q (%T)", bv, b)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if int64(av.Uint()) > bv.Int() {
return av.Uint(), nil
}
return bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if av.Uint() > bv.Uint() {
return av.Uint(), nil
}
return bv.Uint(), nil
case reflect.Float32, reflect.Float64:
if float64(av.Uint()) > bv.Float() {
return av.Uint(), nil
}
return bv.Float(), nil
default:
return nil, fmt.Errorf("maximum: unknown type for %q (%T)", bv, b)
}
case reflect.Float32, reflect.Float64:
switch bv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if av.Float() > float64(bv.Int()) {
return av.Float(), nil
}
return bv.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if av.Float() > float64(bv.Uint()) {
return av.Float(), nil
}
return bv.Uint(), nil
case reflect.Float32, reflect.Float64:
if av.Float() > bv.Float() {
return av.Float(), nil
}
return bv.Float(), nil
default:
return nil, fmt.Errorf("maximum: unknown type for %q (%T)", bv, b)
}
default:
return nil, fmt.Errorf("maximum: unknown type for %q (%T)", av, a)
}
}
// blacklisted always returns an error, to be used in place of blacklisted template functions
func blacklisted(...string) (string, error) {
return "", errors.New("function is disabled")
@@ -1313,10 +1527,18 @@ func pathInSandbox(sandbox, path string) error {
// sockaddr wraps go-sockaddr templating
func sockaddr(args ...string) (string, error) {
t := fmt.Sprintf("{{ %s }} ", strings.Join(args, " "))
t := fmt.Sprintf("{{ %s }}", strings.Join(args, " "))
k, err := socktmpl.Parse(t)
if err != nil {
return "", err
}
return k, nil
}
// sha256Hex return the sha256 hex of a string
func sha256Hex(item string) (string, error) {
h := sha256.New()
h.Write([]byte(item))
output := hex.EncodeToString(h.Sum(nil))
return output, nil
}

View File

@@ -15,12 +15,12 @@ import (
var (
// ErrTemplateContentsAndSource is the error returned when a template
// specifies both a "source" and "content" argument, which is not valid.
ErrTemplateContentsAndSource = errors.New("template: cannot specify both 'source' and 'content'")
ErrTemplateContentsAndSource = errors.New("template: cannot specify both 'source' and 'contents'")
// ErrTemplateMissingContentsAndSource is the error returned when a template
// does not specify either a "source" or "content" argument, which is not
// valid.
ErrTemplateMissingContentsAndSource = errors.New("template: must specify exactly one of 'source' or 'content'")
ErrTemplateMissingContentsAndSource = errors.New("template: must specify exactly one of 'source' or 'contents'")
)
// Template is the internal representation of an individual template to process.
@@ -237,9 +237,12 @@ func funcMap(i *funcMapInput) template.FuncMap {
"secret": secretFunc(i.brain, i.used, i.missing),
"secrets": secretsFunc(i.brain, i.used, i.missing),
"service": serviceFunc(i.brain, i.used, i.missing),
"connect": connectFunc(i.brain, i.used, i.missing),
"services": servicesFunc(i.brain, i.used, i.missing),
"tree": treeFunc(i.brain, i.used, i.missing, true),
"safeTree": safeTreeFunc(i.brain, i.used, i.missing),
"caRoots": connectCARootsFunc(i.brain, i.used, i.missing),
"caLeaf": connectLeafFunc(i.brain, i.used, i.missing),
// Scratch
"scratch": func() *Scratch { return &scratch },
@@ -270,10 +273,12 @@ func funcMap(i *funcMapInput) template.FuncMap {
"parseInt": parseInt,
"parseJSON": parseJSON,
"parseUint": parseUint,
"parseYAML": parseYAML,
"plugin": plugin,
"regexReplaceAll": regexReplaceAll,
"regexMatch": regexMatch,
"replaceAll": replaceAll,
"sha256Hex": sha256Hex,
"timestamp": timestamp,
"toLower": toLower,
"toJSON": toJSON,
@@ -291,6 +296,8 @@ func funcMap(i *funcMapInput) template.FuncMap {
"multiply": multiply,
"divide": divide,
"modulo": modulo,
"minimum": minimum,
"maximum": maximum,
}
for _, bf := range i.functionBlacklist {

View File

@@ -2,7 +2,7 @@ package version
import "fmt"
const Version = "0.22.0"
const Version = "0.25.0"
var (
Name string

View File

@@ -11,11 +11,6 @@ import (
dep "github.com/hashicorp/consul-template/dependency"
)
const (
// The amount of time to do a blocking query for
defaultWaitTime = 60 * time.Second
)
// View is a representation of a Dependency and the most recent data it has
// received from Consul.
type View struct {
@@ -33,6 +28,9 @@ type View struct {
receivedData bool
lastIndex uint64
// blockQueryWaitTime is amount of time in seconds to do a blocking query for
blockQueryWaitTime time.Duration
// maxStale is the maximum amount of time to allow a query to be stale.
maxStale time.Duration
@@ -45,11 +43,6 @@ type View struct {
// stopCh is used to stop polling on this View
stopCh chan struct{}
// vaultGrace is the grace period between a lease and the max TTL for which
// Consul Template will generate a new secret instead of renewing an existing
// one.
vaultGrace time.Duration
}
// NewViewInput is used as input to the NewView function.
@@ -61,6 +54,9 @@ type NewViewInput struct {
// directly to the dependency.
Clients *dep.ClientSet
// BlockQueryWaitTime is amount of time in seconds to do a blocking query for
BlockQueryWaitTime time.Duration
// MaxStale is the maximum amount a time a query response is allowed to be
// stale before forcing a read from the leader.
MaxStale time.Duration
@@ -71,11 +67,6 @@ type NewViewInput struct {
// RetryFunc is a function which dictates how this view should retry on
// upstream errors.
RetryFunc RetryFunc
// VaultGrace is the grace period between a lease and the max TTL for which
// Consul Template will generate a new secret instead of renewing an existing
// one.
VaultGrace time.Duration
}
// NewView constructs a new view with the given inputs.
@@ -83,11 +74,11 @@ func NewView(i *NewViewInput) (*View, error) {
return &View{
dependency: i.Dependency,
clients: i.Clients,
blockQueryWaitTime: i.BlockQueryWaitTime,
maxStale: i.MaxStale,
once: i.Once,
retryFunc: i.RetryFunc,
stopCh: make(chan struct{}, 1),
vaultGrace: i.VaultGrace,
}, nil
}
@@ -212,9 +203,8 @@ func (v *View) fetch(doneCh, successCh chan<- struct{}, errCh chan<- error) {
data, rm, err := v.dependency.Fetch(v.clients, &dep.QueryOptions{
AllowStale: allowStale,
WaitTime: defaultWaitTime,
WaitTime: v.blockQueryWaitTime,
WaitIndex: v.lastIndex,
VaultGrace: v.vaultGrace,
})
if err != nil {
if err == dep.ErrStopped {

View File

@@ -27,6 +27,9 @@ type Watcher struct {
// errCh is the chan where any errors will be published.
errCh chan error
// blockQueryWaitTime is amount of time in seconds to do a blocking query for
blockQueryWaitTime time.Duration
// depViewMap is a map of Templates to Views. Templates are keyed by
// their string.
depViewMap map[string]*View
@@ -42,11 +45,6 @@ type Watcher struct {
retryFuncConsul RetryFunc
retryFuncDefault RetryFunc
retryFuncVault RetryFunc
// vaultGrace is the grace period between a lease and the max TTL for which
// Consul Template will generate a new secret instead of renewing an existing
// one.
vaultGrace time.Duration
}
type NewWatcherInput struct {
@@ -59,6 +57,9 @@ type NewWatcherInput struct {
// Once specifies this watcher should tell views to poll exactly once.
Once bool
// WaitTime is amount of time in seconds to do a blocking query for
BlockQueryWaitTime time.Duration
// RenewVault indicates if this watcher should renew Vault tokens.
RenewVault bool
@@ -72,11 +73,6 @@ type NewWatcherInput struct {
RetryFuncConsul RetryFunc
RetryFuncDefault RetryFunc
RetryFuncVault RetryFunc
// VaultGrace is the grace period between a lease and the max TTL for which
// Consul Template will generate a new secret instead of renewing an existing
// one.
VaultGrace time.Duration
}
// NewWatcher creates a new watcher using the given API client.
@@ -88,10 +84,10 @@ func NewWatcher(i *NewWatcherInput) (*Watcher, error) {
errCh: make(chan error),
maxStale: i.MaxStale,
once: i.Once,
blockQueryWaitTime: i.BlockQueryWaitTime,
retryFuncConsul: i.RetryFuncConsul,
retryFuncDefault: i.RetryFuncDefault,
retryFuncVault: i.RetryFuncVault,
vaultGrace: i.VaultGrace,
}
// Start a watcher for the Vault renew if that config was specified
@@ -163,9 +159,9 @@ func (w *Watcher) Add(d dep.Dependency) (bool, error) {
Dependency: d,
Clients: w.clients,
MaxStale: w.maxStale,
BlockQueryWaitTime: w.blockQueryWaitTime,
Once: w.once,
RetryFunc: retryFunc,
VaultGrace: w.vaultGrace,
})
if err != nil {
return false, errors.Wrap(err, "watcher")

View File

@@ -159,6 +159,14 @@ type AgentServiceRegistration struct {
Namespace string `json:",omitempty" bexpr:"-" hash:"ignore"`
}
//ServiceRegisterOpts is used to pass extra options to the service register.
type ServiceRegisterOpts struct {
//Missing healthchecks will be deleted from the agent.
//Using this parameter allows to idempotently register a service and its checks without
//having to manually deregister checks.
ReplaceExistingChecks bool
}
// AgentCheckRegistration is used to register a new check
type AgentCheckRegistration struct {
ID string `json:",omitempty"`
@@ -182,6 +190,7 @@ type AgentServiceCheck struct {
HTTP string `json:",omitempty"`
Header map[string][]string `json:",omitempty"`
Method string `json:",omitempty"`
Body string `json:",omitempty"`
TCP string `json:",omitempty"`
Status string `json:",omitempty"`
Notes string `json:",omitempty"`
@@ -554,8 +563,25 @@ func (a *Agent) MembersOpts(opts MembersOpts) ([]*AgentMember, error) {
// ServiceRegister is used to register a new service with
// the local agent
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
opts := ServiceRegisterOpts{
ReplaceExistingChecks: false,
}
return a.serviceRegister(service, opts)
}
// ServiceRegister is used to register a new service with
// the local agent and can be passed additional options.
func (a *Agent) ServiceRegisterOpts(service *AgentServiceRegistration, opts ServiceRegisterOpts) error {
return a.serviceRegister(service, opts)
}
func (a *Agent) serviceRegister(service *AgentServiceRegistration, opts ServiceRegisterOpts) error {
r := a.c.newRequest("PUT", "/v1/agent/service/register")
r.obj = service
if opts.ReplaceExistingChecks {
r.params.Set("replace-existing-checks", "true")
}
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return err
@@ -870,20 +896,29 @@ func (a *Agent) DisableNodeMaintenance() error {
// log stream. An empty string will be sent down the given channel when there's
// nothing left to stream, after which the caller should close the stopCh.
func (a *Agent) Monitor(loglevel string, stopCh <-chan struct{}, q *QueryOptions) (chan string, error) {
return a.monitor(loglevel, false, stopCh, q)
}
// MonitorJSON is like Monitor except it returns logs in JSON format.
func (a *Agent) MonitorJSON(loglevel string, stopCh <-chan struct{}, q *QueryOptions) (chan string, error) {
return a.monitor(loglevel, true, stopCh, q)
}
func (a *Agent) monitor(loglevel string, logJSON bool, stopCh <-chan struct{}, q *QueryOptions) (chan string, error) {
r := a.c.newRequest("GET", "/v1/agent/monitor")
r.setQueryOptions(q)
if loglevel != "" {
r.params.Add("loglevel", loglevel)
}
if logJSON {
r.params.Set("logjson", "true")
}
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return nil, err
}
logCh := make(chan string, 64)
go func() {
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for {
select {
@@ -907,7 +942,6 @@ func (a *Agent) Monitor(loglevel string, stopCh <-chan struct{}, q *QueryOptions
}
}
}()
return logCh, nil
}

View File

@@ -8,7 +8,6 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/url"
@@ -18,6 +17,7 @@ import (
"time"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-rootcerts"
)
@@ -358,7 +358,14 @@ type TLSConfig struct {
// is not recommended, then you may notice idle connections building up over
// time. To avoid this, use the DefaultNonPooledConfig() instead.
func DefaultConfig() *Config {
return defaultConfig(cleanhttp.DefaultPooledTransport)
return defaultConfig(nil, cleanhttp.DefaultPooledTransport)
}
// DefaultConfigWithLogger returns a default configuration for the client. It
// is exactly the same as DefaultConfig, but allows for a pre-configured logger
// object to be passed through.
func DefaultConfigWithLogger(logger hclog.Logger) *Config {
return defaultConfig(logger, cleanhttp.DefaultPooledTransport)
}
// DefaultNonPooledConfig returns a default configuration for the client which
@@ -367,12 +374,18 @@ func DefaultConfig() *Config {
// accumulation of idle connections if you make many client objects during the
// lifetime of your application.
func DefaultNonPooledConfig() *Config {
return defaultConfig(cleanhttp.DefaultTransport)
return defaultConfig(nil, cleanhttp.DefaultTransport)
}
// defaultConfig returns the default configuration for the client, using the
// given function to make the transport.
func defaultConfig(transportFn func() *http.Transport) *Config {
func defaultConfig(logger hclog.Logger, transportFn func() *http.Transport) *Config {
if logger == nil {
logger = hclog.New(&hclog.LoggerOptions{
Name: "consul-api",
})
}
config := &Config{
Address: "127.0.0.1:8500",
Scheme: "http",
@@ -410,7 +423,7 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
if ssl := os.Getenv(HTTPSSLEnvName); ssl != "" {
enabled, err := strconv.ParseBool(ssl)
if err != nil {
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLEnvName, err)
logger.Warn(fmt.Sprintf("could not parse %s", HTTPSSLEnvName), "error", err)
}
if enabled {
@@ -436,7 +449,7 @@ func defaultConfig(transportFn func() *http.Transport) *Config {
if v := os.Getenv(HTTPSSLVerifyEnvName); v != "" {
doVerify, err := strconv.ParseBool(v)
if err != nil {
log.Printf("[WARN] client: could not parse %s: %s", HTTPSSLVerifyEnvName, err)
logger.Warn(fmt.Sprintf("could not parse %s", HTTPSSLVerifyEnvName), "error", err)
}
if !doVerify {
config.TLSConfig.InsecureSkipVerify = true

View File

@@ -33,7 +33,6 @@ func (d *DiscoveryChain) Get(name string, opts *DiscoveryChainOptions, q *QueryO
if opts.EvaluateInDatacenter != "" {
r.params.Set("compile-dc", opts.EvaluateInDatacenter)
}
// TODO(namespaces): handle possible EvaluateInNamespace here
}
if method == "POST" {

View File

@@ -5,11 +5,12 @@ go 1.12
replace github.com/hashicorp/consul/sdk => ../sdk
require (
github.com/hashicorp/consul/sdk v0.2.0
github.com/hashicorp/consul/sdk v0.4.0
github.com/hashicorp/go-cleanhttp v0.5.1
github.com/hashicorp/go-hclog v0.12.0
github.com/hashicorp/go-rootcerts v1.0.2
github.com/hashicorp/go-uuid v1.0.1
github.com/hashicorp/serf v0.8.2
github.com/mitchellh/mapstructure v1.1.2
github.com/stretchr/testify v1.3.0
github.com/stretchr/testify v1.4.0
)

View File

@@ -6,21 +6,26 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30=
github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
@@ -38,13 +43,24 @@ github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG67
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
@@ -68,6 +84,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -78,5 +96,18 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII=
golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -51,6 +51,7 @@ type HealthCheckDefinition struct {
HTTP string
Header map[string][]string
Method string
Body string
TLSSkipVerify bool
TCP string
IntervalDuration time.Duration `json:"-"`

View File

@@ -25,6 +25,9 @@ type ServiceQuery struct {
// Service is the service to query.
Service string
// Namespace of the service to query
Namespace string `json:",omitempty"`
// Near allows baking in the name of a node to automatically distance-
// sort from. The magic "_agent" value is supported, which sorts near
// the agent which initiated the request by default.
@@ -119,6 +122,9 @@ type PreparedQueryExecuteResponse struct {
// Service is the service that was queried.
Service string
// Namespace of the service that was queried
Namespace string `json:",omitempty"`
// Nodes has the nodes that were output by the query.
Nodes []ServiceEntry

View File

@@ -81,6 +81,7 @@ type KVTxnOp struct {
Flags uint64
Index uint64
Session string
Namespace string `json:",omitempty"`
}
// KVTxnOps defines a set of operations to be performed inside a single

View File

@@ -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=
@@ -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=
@@ -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=

View File

@@ -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.

64
vendor/github.com/hashicorp/vault/api/sys_monitor.go generated vendored Normal file
View File

@@ -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
}

View File

@@ -171,10 +171,9 @@ func (c *Client) GetUserDN(cfg *ConfigEntry, conn Connection, bindDN, username s
if err != nil {
return userDN, errwrap.Wrapf("LDAP search failed for detecting user: {{err}}", err)
}
if len(result.Entries) != 1 {
return userDN, fmt.Errorf("LDAP search for userdn 0 or not unique")
for _, e := range result.Entries {
userDN = e.DN
}
userDN = result.Entries[0].DN
} else {
userDN = bindDN
}

View File

@@ -185,13 +185,13 @@ func RespondWithStatusCode(resp *Response, req *Request, code int) (*Response, e
}
// HTTPResponseWriter is optionally added to a request object and can be used to
// write directly to the HTTP response writter.
// write directly to the HTTP response writer.
type HTTPResponseWriter struct {
http.ResponseWriter
written *uint32
}
// NewHTTPResponseWriter creates a new HTTPRepoinseWriter object that wraps the
// NewHTTPResponseWriter creates a new HTTPResponseWriter object that wraps the
// provided io.Writer.
func NewHTTPResponseWriter(w http.ResponseWriter) *HTTPResponseWriter {
return &HTTPResponseWriter{

View File

@@ -1,5 +1,3 @@
The MIT License (MIT)
Copyright 2012 Keith Rarick
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@@ -125,7 +125,6 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) {
}
keys := v.MapKeys()
for i := 0; i < v.Len(); i++ {
showTypeInStruct := true
k := keys[i]
mv := v.MapIndex(k)
pp.printValue(k, false, true)
@@ -133,7 +132,7 @@ func (p *printer) printValue(v reflect.Value, showType, quote bool) {
if expand {
writeByte(pp, '\t')
}
showTypeInStruct = t.Elem().Kind() == reflect.Interface
showTypeInStruct := t.Elem().Kind() == reflect.Interface
pp.printValue(mv, showTypeInStruct, true)
if expand {
io.WriteString(pp, ",\n")

6
vendor/github.com/kr/pretty/go.mod generated vendored
View File

@@ -1,3 +1,5 @@
module "github.com/kr/pretty"
module github.com/kr/pretty
require "github.com/kr/text" v0.1.0
go 1.12
require github.com/kr/text v0.1.0

3
vendor/github.com/kr/pretty/go.sum generated vendored Normal file
View File

@@ -0,0 +1,3 @@
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=

View File

@@ -75,7 +75,7 @@ func Printf(format string, a ...interface{}) (n int, errno error) {
// Println pretty-prints its operands and writes to standard output.
//
// Calling Print(x, y) is equivalent to
// Calling Println(x, y) is equivalent to
// fmt.Println(Formatter(x), Formatter(y)), but each operand is
// formatted with "%# v".
func Println(a ...interface{}) (n int, errno error) {

View File

@@ -1,9 +1,15 @@
language: go
sudo: false
go:
- 1.13.x
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
- go get -t -v ./...
script:
- $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
- ./go.test.sh
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@@ -1,8 +1,8 @@
# go-colorable
[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master)
[![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable)
[![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable)
Colorable writer for windows.

View File

@@ -27,3 +27,11 @@ func NewColorableStdout() io.Writer {
func NewColorableStderr() io.Writer {
return os.Stderr
}
// EnableColorsStdout enable colors if possible.
func EnableColorsStdout(enabled *bool) func() {
if enabled != nil {
*enabled = true
}
return func() {}
}

View File

@@ -28,3 +28,11 @@ func NewColorableStdout() io.Writer {
func NewColorableStderr() io.Writer {
return os.Stderr
}
// EnableColorsStdout enable colors if possible.
func EnableColorsStdout(enabled *bool) func() {
if enabled != nil {
*enabled = true
}
return func() {}
}

View File

@@ -27,6 +27,8 @@ const (
backgroundRed = 0x40
backgroundIntensity = 0x80
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
)
const (
@@ -78,6 +80,8 @@ var (
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer")
)
@@ -98,6 +102,10 @@ func NewColorable(file *os.File) io.Writer {
}
if isatty.IsTerminal(file.Fd()) {
var mode uint32
if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 {
return file
}
var csbi consoleScreenBufferInfo
handle := syscall.Handle(file.Fd())
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
@@ -1003,3 +1011,23 @@ func n256setup() {
n256backAttr[i] = c.backgroundAttr()
}
}
// EnableColorsStdout enable colors if possible.
func EnableColorsStdout(enabled *bool) func() {
var mode uint32
h := os.Stdout.Fd()
if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 {
if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 {
if enabled != nil {
*enabled = true
}
return func() {
procSetConsoleMode.Call(h, uintptr(mode))
}
}
}
if enabled != nil {
*enabled = true
}
return func() {}
}

View File

@@ -1,3 +1,8 @@
module github.com/mattn/go-colorable
require github.com/mattn/go-isatty v0.0.8
require (
github.com/mattn/go-isatty v0.0.12
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
)
go 1.13

View File

@@ -1,4 +1,5 @@
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

12
vendor/github.com/mattn/go-colorable/go.test.sh generated vendored Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
echo "" > coverage.txt
for d in $(go list ./... | grep -v vendor); do
go test -race -coverprofile=profile.out -covermode=atomic "$d"
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out
fi
done

View File

@@ -1,13 +1,14 @@
language: go
sudo: false
go:
- 1.13.x
- tip
os:
- linux
- osx
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
- go get -t -v ./...
script:
- $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5
- ./go.test.sh
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@@ -1,7 +1,7 @@
# go-isatty
[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty)
[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty)
[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty)

View File

@@ -1,5 +1,5 @@
module github.com/mattn/go-isatty
require golang.org/x/sys v0.0.0-20191008105621-543471e840be
go 1.12
go 1.14
require golang.org/x/sys v0.0.0-20200116001909-b77594299b42

View File

@@ -1,4 +1,2 @@
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

12
vendor/github.com/mattn/go-isatty/go.test.sh generated vendored Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
echo "" > coverage.txt
for d in $(go list ./... | grep -v vendor); do
go test -race -coverprofile=profile.out -covermode=atomic "$d"
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
rm profile.out
fi
done

View File

@@ -1,23 +0,0 @@
// +build android
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TCGETS
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
}
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
// terminal. This is also always false on this environment.
func IsCygwinTerminal(fd uintptr) bool {
return false
}

View File

@@ -3,18 +3,12 @@
package isatty
import (
"syscall"
"unsafe"
)
const ioctlReadTermios = syscall.TIOCGETA
import "golang.org/x/sys/unix"
// IsTerminal return true if the file descriptor is terminal.
func IsTerminal(fd uintptr) bool {
var termios syscall.Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0
_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
return err == nil
}
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2

View File

@@ -8,7 +8,7 @@ import (
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
path, err := syscall.Fd2path(fd)
path, err := syscall.Fd2path(int(fd))
if err != nil {
return false
}

View File

@@ -1,6 +1,5 @@
// +build linux aix
// +build !appengine
// +build !android
package isatty

8
vendor/github.com/mattn/go-isatty/renovate.json generated vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"extends": [
"config:base"
],
"postUpdateOptions": [
"gomodTidy"
]
}

18
vendor/gopkg.in/yaml.v2/.travis.yml generated vendored
View File

@@ -1,12 +1,16 @@
language: go
go:
- 1.4
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
- tip
- "1.4.x"
- "1.5.x"
- "1.6.x"
- "1.7.x"
- "1.8.x"
- "1.9.x"
- "1.10.x"
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "tip"
go_import_path: gopkg.in/yaml.v2

89
vendor/gopkg.in/yaml.v2/scannerc.go generated vendored
View File

@@ -626,31 +626,18 @@ func trace(args ...interface{}) func() {
func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
// While we need more tokens to fetch, do it.
for {
// Check if we really need to fetch more tokens.
need_more_tokens := false
if parser.tokens_head == len(parser.tokens) {
// Queue is empty.
need_more_tokens = true
} else {
// Check if any potential simple key may occupy the head position.
if !yaml_parser_stale_simple_keys(parser) {
if parser.tokens_head != len(parser.tokens) {
// If queue is non-empty, check if any potential simple key may
// occupy the head position.
head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
if !ok {
break
} else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok {
return false
}
for i := range parser.simple_keys {
simple_key := &parser.simple_keys[i]
if simple_key.possible && simple_key.token_number == parser.tokens_parsed {
need_more_tokens = true
} else if !valid {
break
}
}
}
// We are finished.
if !need_more_tokens {
break
}
// Fetch the next token.
if !yaml_parser_fetch_next_token(parser) {
return false
@@ -678,11 +665,6 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
return false
}
// Remove obsolete potential simple keys.
if !yaml_parser_stale_simple_keys(parser) {
return false
}
// Check the indentation level against the current column.
if !yaml_parser_unroll_indent(parser, parser.mark.column) {
return false
@@ -837,29 +819,30 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
"found character that cannot start any token")
}
// Check the list of potential simple keys and remove the positions that
// cannot contain simple keys anymore.
func yaml_parser_stale_simple_keys(parser *yaml_parser_t) bool {
// Check for a potential simple key for each flow level.
for i := range parser.simple_keys {
simple_key := &parser.simple_keys[i]
func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) {
if !simple_key.possible {
return false, true
}
// The specification requires that a simple key
// The 1.2 specification says:
//
// - is limited to a single line,
// - is shorter than 1024 characters.
if simple_key.possible && (simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index) {
// "If the ? indicator is omitted, parsing needs to see past the
// implicit key to recognize it as such. To limit the amount of
// lookahead required, the “:” indicator must appear at most 1024
// Unicode characters beyond the start of the key. In addition, the key
// is restricted to a single line."
//
if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index {
// Check if the potential simple key to be removed is required.
if simple_key.required {
return yaml_parser_set_scanner_error(parser,
return false, yaml_parser_set_scanner_error(parser,
"while scanning a simple key", simple_key.mark,
"could not find expected ':'")
}
simple_key.possible = false
return false, true
}
}
return true
return true, true
}
// Check if a simple key may start at the current position and add it if
@@ -879,13 +862,14 @@ func yaml_parser_save_simple_key(parser *yaml_parser_t) bool {
possible: true,
required: required,
token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
mark: parser.mark,
}
simple_key.mark = parser.mark
if !yaml_parser_remove_simple_key(parser) {
return false
}
parser.simple_keys[len(parser.simple_keys)-1] = simple_key
parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1
}
return true
}
@@ -900,9 +884,10 @@ func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool {
"while scanning a simple key", parser.simple_keys[i].mark,
"could not find expected ':'")
}
}
// Remove the key from the stack.
parser.simple_keys[i].possible = false
delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number)
}
return true
}
@@ -912,7 +897,12 @@ const max_flow_level = 10000
// Increase the flow level and resize the simple key list if needed.
func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
// Reset the simple key on the next level.
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{
possible: false,
required: false,
token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
mark: parser.mark,
})
// Increase the flow level.
parser.flow_level++
@@ -928,7 +918,9 @@ func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool {
if parser.flow_level > 0 {
parser.flow_level--
parser.simple_keys = parser.simple_keys[:len(parser.simple_keys)-1]
last := len(parser.simple_keys) - 1
delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number)
parser.simple_keys = parser.simple_keys[:last]
}
return true
}
@@ -1005,6 +997,8 @@ func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool {
// Initialize the simple key stack.
parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
parser.simple_keys_by_tok = make(map[int]int)
// A simple key is allowed at the beginning of the stream.
parser.simple_key_allowed = true
@@ -1286,7 +1280,11 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
simple_key := &parser.simple_keys[len(parser.simple_keys)-1]
// Have we found a simple key?
if simple_key.possible {
if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok {
return false
} else if valid {
// Create the KEY token and insert it into the queue.
token := yaml_token_t{
typ: yaml_KEY_TOKEN,
@@ -1304,6 +1302,7 @@ func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
// Remove the simple key.
simple_key.possible = false
delete(parser.simple_keys_by_tok, simple_key.token_number)
// A simple key cannot follow another simple key.
parser.simple_key_allowed = false

2
vendor/gopkg.in/yaml.v2/yaml.go generated vendored
View File

@@ -89,7 +89,7 @@ func UnmarshalStrict(in []byte, out interface{}) (err error) {
return unmarshal(in, out, true)
}
// A Decorder reads and decodes YAML values from an input stream.
// A Decoder reads and decodes YAML values from an input stream.
type Decoder struct {
strict bool
parser *parser

1
vendor/gopkg.in/yaml.v2/yamlh.go generated vendored
View File

@@ -579,6 +579,7 @@ type yaml_parser_t struct {
simple_key_allowed bool // May a simple key occur at the current position?
simple_keys []yaml_simple_key_t // The stack of simple keys.
simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number
// Parser stuff

14
vendor/modules.txt vendored
View File

@@ -233,7 +233,7 @@ github.com/duosecurity/duo_api_golang
github.com/duosecurity/duo_api_golang/authapi
# github.com/elazarl/go-bindata-assetfs v1.0.1-0.20200509193318-234c15e7648f
github.com/elazarl/go-bindata-assetfs
# github.com/fatih/color v1.7.0
# github.com/fatih/color v1.9.0
github.com/fatih/color
# github.com/fatih/structs v1.1.0
github.com/fatih/structs
@@ -313,7 +313,7 @@ github.com/gorhill/cronexpr
github.com/gorilla/websocket
# github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed
github.com/hailocab/go-hostpool
# github.com/hashicorp/consul-template v0.22.0
# github.com/hashicorp/consul-template v0.25.0
github.com/hashicorp/consul-template/child
github.com/hashicorp/consul-template/config
github.com/hashicorp/consul-template/dependency
@@ -324,7 +324,7 @@ github.com/hashicorp/consul-template/signals
github.com/hashicorp/consul-template/template
github.com/hashicorp/consul-template/version
github.com/hashicorp/consul-template/watch
# github.com/hashicorp/consul/api v1.2.1-0.20200128105449-6681be918a6e
# github.com/hashicorp/consul/api v1.4.0
github.com/hashicorp/consul/api
# github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/errwrap
@@ -596,7 +596,7 @@ github.com/keybase/go-crypto/openpgp/s2k
github.com/keybase/go-crypto/rsa
# github.com/konsorten/go-windows-terminal-sequences v1.0.1
github.com/konsorten/go-windows-terminal-sequences
# github.com/kr/pretty v0.1.0
# github.com/kr/pretty v0.2.0
github.com/kr/pretty
# github.com/kr/text v0.1.0
github.com/kr/text
@@ -604,9 +604,9 @@ github.com/kr/text
github.com/lib/pq
github.com/lib/pq/oid
github.com/lib/pq/scram
# github.com/mattn/go-colorable v0.1.4
# github.com/mattn/go-colorable v0.1.6
github.com/mattn/go-colorable
# github.com/mattn/go-isatty v0.0.10
# github.com/mattn/go-isatty v0.0.12
github.com/mattn/go-isatty
# github.com/mattn/go-shellwords v1.0.5
github.com/mattn/go-shellwords
@@ -1110,7 +1110,7 @@ gopkg.in/square/go-jose.v2
gopkg.in/square/go-jose.v2/cipher
gopkg.in/square/go-jose.v2/json
gopkg.in/square/go-jose.v2/jwt
# gopkg.in/yaml.v2 v2.2.5
# gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2
# honnef.co/go/tools v0.0.1-2020.1.3
honnef.co/go/tools/arg