Merge Consul OSS branch 'master' at commit 4eb73973b6

pull/5987/head
hashicorp-ci 2019-06-27 02:00:41 +00:00
commit f4304e2e5b
30 changed files with 592 additions and 206 deletions

View File

@ -4,9 +4,12 @@ IMPROVEMENTS
* agent: improve startup message when no error occurs [[GH-5896](https://github.com/hashicorp/consul/issues/5896)] * agent: improve startup message when no error occurs [[GH-5896](https://github.com/hashicorp/consul/issues/5896)]
* agent: make sure client agent rate limits apply when hitting the client interface on a server directly [[GH-5927](https://github.com/hashicorp/consul/pull/5927)] * agent: make sure client agent rate limits apply when hitting the client interface on a server directly [[GH-5927](https://github.com/hashicorp/consul/pull/5927)]
* agent: use stale requests when performing full sync [[GH-5873](https://github.com/hashicorp/consul/pull/5873)]
* agent: transfer leadership when establishLeadership fails [[GH-5247](https://github.com/hashicorp/consul/pull/5247)]
* connect: provide -admin-access-log-path for envoy [[GH-5858](https://github.com/hashicorp/consul/pull/5858)] * connect: provide -admin-access-log-path for envoy [[GH-5858](https://github.com/hashicorp/consul/pull/5858)]
* connect: upgrade Envoy xDS protocol to support Envoy 1.10 [[GH-5872](https://github.com/hashicorp/consul/pull/5872)] * connect: upgrade Envoy xDS protocol to support Envoy 1.10 [[GH-5872](https://github.com/hashicorp/consul/pull/5872)]
* ui: Improve linking between sidecars and proxies and their services/service instances [[GH-5944](https://github.com/hashicorp/consul/pull/5944)]
* ui: Add ability to search for tokens by policy, role or service identity name [[GH-5811](https://github.com/hashicorp/consul/pull/5811)]
BUG FIXES: BUG FIXES:
@ -14,6 +17,7 @@ BUG FIXES:
* api: update link to agent caching in comments [[GH-5935](https://github.com/hashicorp/consul/pull/5935)] * api: update link to agent caching in comments [[GH-5935](https://github.com/hashicorp/consul/pull/5935)]
* connect: fix proxy address formatting for IPv6 addresses [[GH-5460](https://github.com/hashicorp/consul/issues/5460)] * connect: fix proxy address formatting for IPv6 addresses [[GH-5460](https://github.com/hashicorp/consul/issues/5460)]
* ui: fix service instance linking when multiple non-unique service id's exist on multiple nodes [[GH-5933](https://github.com/hashicorp/consul/pull/5933)] * ui: fix service instance linking when multiple non-unique service id's exist on multiple nodes [[GH-5933](https://github.com/hashicorp/consul/pull/5933)]
* ui: Improve error messaging for ACL policies [[GH-5836](https://github.com/hashicorp/consul/pull/5836)]
* txn: Fixed an issue that would allow a CAS operation on a service to work when it shouldn't have. [[GH-5971](https://github.com/hashicorp/consul/pull/5971)] * txn: Fixed an issue that would allow a CAS operation on a service to work when it shouldn't have. [[GH-5971](https://github.com/hashicorp/consul/pull/5971)]
## 1.5.1 (May 22, 2019) ## 1.5.1 (May 22, 2019)

View File

@ -915,6 +915,7 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
base.CoordinateUpdateBatchSize = a.config.ConsulCoordinateUpdateBatchSize base.CoordinateUpdateBatchSize = a.config.ConsulCoordinateUpdateBatchSize
base.CoordinateUpdateMaxBatches = a.config.ConsulCoordinateUpdateMaxBatches base.CoordinateUpdateMaxBatches = a.config.ConsulCoordinateUpdateMaxBatches
base.CoordinateUpdatePeriod = a.config.ConsulCoordinateUpdatePeriod base.CoordinateUpdatePeriod = a.config.ConsulCoordinateUpdatePeriod
base.CheckOutputMaxSize = a.config.CheckOutputMaxSize
base.RaftConfig.HeartbeatTimeout = a.config.ConsulRaftHeartbeatTimeout base.RaftConfig.HeartbeatTimeout = a.config.ConsulRaftHeartbeatTimeout
base.RaftConfig.LeaderLeaseTimeout = a.config.ConsulRaftLeaderLeaseTimeout base.RaftConfig.LeaderLeaseTimeout = a.config.ConsulRaftLeaderLeaseTimeout
@ -971,6 +972,9 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
if a.config.Bootstrap { if a.config.Bootstrap {
base.Bootstrap = true base.Bootstrap = true
} }
if a.config.CheckOutputMaxSize > 0 {
base.CheckOutputMaxSize = a.config.CheckOutputMaxSize
}
if a.config.RejoinAfterLeave { if a.config.RejoinAfterLeave {
base.RejoinAfterLeave = true base.RejoinAfterLeave = true
} }
@ -2248,6 +2252,13 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
// Check if already registered // Check if already registered
if chkType != nil { if chkType != nil {
maxOutputSize := a.config.CheckOutputMaxSize
if maxOutputSize == 0 {
maxOutputSize = checks.DefaultBufSize
}
if chkType.OutputMaxSize > 0 && maxOutputSize > chkType.OutputMaxSize {
maxOutputSize = chkType.OutputMaxSize
}
switch { switch {
case chkType.IsTTL(): case chkType.IsTTL():
@ -2257,10 +2268,11 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
} }
ttl := &checks.CheckTTL{ ttl := &checks.CheckTTL{
Notify: a.State, Notify: a.State,
CheckID: check.CheckID, CheckID: check.CheckID,
TTL: chkType.TTL, TTL: chkType.TTL,
Logger: a.logger, Logger: a.logger,
OutputMaxSize: maxOutputSize,
} }
// Restore persisted state, if any // Restore persisted state, if any
@ -2294,6 +2306,7 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
Interval: chkType.Interval, Interval: chkType.Interval,
Timeout: chkType.Timeout, Timeout: chkType.Timeout,
Logger: a.logger, Logger: a.logger,
OutputMaxSize: maxOutputSize,
TLSClientConfig: tlsClientConfig, TLSClientConfig: tlsClientConfig,
} }
http.Start() http.Start()
@ -2361,7 +2374,7 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
} }
if a.dockerClient == nil { if a.dockerClient == nil {
dc, err := checks.NewDockerClient(os.Getenv("DOCKER_HOST"), checks.BufSize) dc, err := checks.NewDockerClient(os.Getenv("DOCKER_HOST"), int64(maxOutputSize))
if err != nil { if err != nil {
a.logger.Printf("[ERR] agent: error creating docker client: %s", err) a.logger.Printf("[ERR] agent: error creating docker client: %s", err)
return err return err
@ -2396,14 +2409,14 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
check.CheckID, checks.MinInterval) check.CheckID, checks.MinInterval)
chkType.Interval = checks.MinInterval chkType.Interval = checks.MinInterval
} }
monitor := &checks.CheckMonitor{ monitor := &checks.CheckMonitor{
Notify: a.State, Notify: a.State,
CheckID: check.CheckID, CheckID: check.CheckID,
ScriptArgs: chkType.ScriptArgs, ScriptArgs: chkType.ScriptArgs,
Interval: chkType.Interval, Interval: chkType.Interval,
Timeout: chkType.Timeout, Timeout: chkType.Timeout,
Logger: a.logger, Logger: a.logger,
OutputMaxSize: maxOutputSize,
} }
monitor.Start() monitor.Start()
a.checkMonitors[check.CheckID] = monitor a.checkMonitors[check.CheckID] = monitor
@ -2878,7 +2891,7 @@ func (a *Agent) updateTTLCheck(checkID types.CheckID, status, output string) err
} }
// Set the status through CheckTTL to reset the TTL. // Set the status through CheckTTL to reset the TTL.
check.SetStatus(status, output) outputTruncated := check.SetStatus(status, output)
// We don't write any files in dev mode so bail here. // We don't write any files in dev mode so bail here.
if a.config.DataDir == "" { if a.config.DataDir == "" {
@ -2887,7 +2900,7 @@ func (a *Agent) updateTTLCheck(checkID types.CheckID, status, output string) err
// Persist the state so the TTL check can come up in a good state after // Persist the state so the TTL check can come up in a good state after
// an agent restart, especially with long TTL values. // an agent restart, especially with long TTL values.
if err := a.persistCheckState(check, status, output); err != nil { if err := a.persistCheckState(check, status, outputTruncated); err != nil {
return fmt.Errorf("failed persisting state for check %q: %s", checkID, err) return fmt.Errorf("failed persisting state for check %q: %s", checkID, err)
} }

View File

@ -19,7 +19,6 @@ import (
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
cachetype "github.com/hashicorp/consul/agent/cache-types" cachetype "github.com/hashicorp/consul/agent/cache-types"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/debug" "github.com/hashicorp/consul/agent/debug"
"github.com/hashicorp/consul/agent/local" "github.com/hashicorp/consul/agent/local"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
@ -725,12 +724,6 @@ func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Reques
return nil, nil return nil, nil
} }
total := len(update.Output)
if total > checks.BufSize {
update.Output = fmt.Sprintf("%s ... (captured %d of %d bytes)",
update.Output[:checks.BufSize], checks.BufSize, total)
}
checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/")) checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/"))
// Get the provided token, if any, and vet against any ACL policies. // Get the provided token, if any, and vet against any ACL policies.

View File

@ -18,7 +18,6 @@ import (
"time" "time"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/config" "github.com/hashicorp/consul/agent/config"
"github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/debug" "github.com/hashicorp/consul/agent/debug"
@ -2299,7 +2298,8 @@ func TestAgent_FailCheck_ACLDeny(t *testing.T) {
func TestAgent_UpdateCheck(t *testing.T) { func TestAgent_UpdateCheck(t *testing.T) {
t.Parallel() t.Parallel()
a := NewTestAgent(t, t.Name(), "") maxChecksSize := 256
a := NewTestAgent(t, t.Name(), fmt.Sprintf("check_output_max_size=%d", maxChecksSize))
defer a.Shutdown() defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1") testrpc.WaitForTestAgent(t, a.RPC, "dc1")
@ -2340,7 +2340,7 @@ func TestAgent_UpdateCheck(t *testing.T) {
t.Run("log output limit", func(t *testing.T) { t.Run("log output limit", func(t *testing.T) {
args := checkUpdate{ args := checkUpdate{
Status: api.HealthPassing, Status: api.HealthPassing,
Output: strings.Repeat("-= bad -=", 5*checks.BufSize), Output: strings.Repeat("-= bad -=", 5*maxChecksSize),
} }
req, _ := http.NewRequest("PUT", "/v1/agent/check/update/test", jsonReader(args)) req, _ := http.NewRequest("PUT", "/v1/agent/check/update/test", jsonReader(args))
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
@ -2359,8 +2359,8 @@ func TestAgent_UpdateCheck(t *testing.T) {
// rough check that the output buffer was cut down so this test // rough check that the output buffer was cut down so this test
// isn't super brittle. // isn't super brittle.
state := a.State.Checks()["test"] state := a.State.Checks()["test"]
if state.Status != api.HealthPassing || len(state.Output) > 2*checks.BufSize { if state.Status != api.HealthPassing || len(state.Output) > 2*maxChecksSize {
t.Fatalf("bad: %v", state) t.Fatalf("bad: %v, (len:=%d)", state, len(state.Output))
} }
}) })

View File

@ -1586,7 +1586,7 @@ func TestAgent_updateTTLCheck(t *testing.T) {
t.Parallel() t.Parallel()
a := NewTestAgent(t, t.Name(), "") a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown() defer a.Shutdown()
checkBufSize := 100
health := &structs.HealthCheck{ health := &structs.HealthCheck{
Node: "foo", Node: "foo",
CheckID: "mem", CheckID: "mem",
@ -1594,7 +1594,8 @@ func TestAgent_updateTTLCheck(t *testing.T) {
Status: api.HealthCritical, Status: api.HealthCritical,
} }
chk := &structs.CheckType{ chk := &structs.CheckType{
TTL: 15 * time.Second, TTL: 15 * time.Second,
OutputMaxSize: checkBufSize,
} }
// Add check and update it. // Add check and update it.
@ -1614,6 +1615,19 @@ func TestAgent_updateTTLCheck(t *testing.T) {
if status.Output != "foo" { if status.Output != "foo" {
t.Fatalf("bad: %v", status) t.Fatalf("bad: %v", status)
} }
if err := a.updateTTLCheck("mem", api.HealthCritical, strings.Repeat("--bad-- ", 5*checkBufSize)); err != nil {
t.Fatalf("err: %v", err)
}
// Ensure we have a check mapping.
status = a.State.Checks()["mem"]
if status.Status != api.HealthCritical {
t.Fatalf("bad: %v", status)
}
if len(status.Output) > checkBufSize*2 {
t.Fatalf("bad: %v", len(status.Output))
}
} }
func TestAgent_PersistService(t *testing.T) { func TestAgent_PersistService(t *testing.T) {

File diff suppressed because one or more lines are too long

View File

@ -28,10 +28,10 @@ const (
// Otherwise we risk fork bombing a system. // Otherwise we risk fork bombing a system.
MinInterval = time.Second MinInterval = time.Second
// BufSize is the maximum size of the captured // DefaultBufSize is the maximum size of the captured
// check output. Prevents an enormous buffer // check output by defaut. Prevents an enormous buffer
// from being captured // from being captured
BufSize = 4 * 1024 // 4KB DefaultBufSize = 4 * 1024 // 4KB
// UserAgent is the value of the User-Agent header // UserAgent is the value of the User-Agent header
// for HTTP health checks. // for HTTP health checks.
@ -56,13 +56,14 @@ type CheckNotifier interface {
// determine the health of a given check. It is compatible with // determine the health of a given check. It is compatible with
// nagios plugins and expects the output in the same format. // nagios plugins and expects the output in the same format.
type CheckMonitor struct { type CheckMonitor struct {
Notify CheckNotifier Notify CheckNotifier
CheckID types.CheckID CheckID types.CheckID
Script string Script string
ScriptArgs []string ScriptArgs []string
Interval time.Duration Interval time.Duration
Timeout time.Duration Timeout time.Duration
Logger *log.Logger Logger *log.Logger
OutputMaxSize int
stop bool stop bool
stopCh chan struct{} stopCh chan struct{}
@ -122,7 +123,7 @@ func (c *CheckMonitor) check() {
} }
// Collect the output // Collect the output
output, _ := circbuf.NewBuffer(BufSize) output, _ := circbuf.NewBuffer(int64(c.OutputMaxSize))
cmd.Stdout = output cmd.Stdout = output
cmd.Stderr = output cmd.Stderr = output
exec.SetSysProcAttr(cmd) exec.SetSysProcAttr(cmd)
@ -222,12 +223,17 @@ type CheckTTL struct {
stop bool stop bool
stopCh chan struct{} stopCh chan struct{}
stopLock sync.Mutex stopLock sync.Mutex
OutputMaxSize int
} }
// Start is used to start a check ttl, runs until Stop() // Start is used to start a check ttl, runs until Stop()
func (c *CheckTTL) Start() { func (c *CheckTTL) Start() {
c.stopLock.Lock() c.stopLock.Lock()
defer c.stopLock.Unlock() defer c.stopLock.Unlock()
if c.OutputMaxSize < 1 {
c.OutputMaxSize = DefaultBufSize
}
c.stop = false c.stop = false
c.stopCh = make(chan struct{}) c.stopCh = make(chan struct{})
c.timer = time.NewTimer(c.TTL) c.timer = time.NewTimer(c.TTL)
@ -275,16 +281,22 @@ func (c *CheckTTL) getExpiredOutput() string {
// SetStatus is used to update the status of the check, // SetStatus is used to update the status of the check,
// and to renew the TTL. If expired, TTL is restarted. // and to renew the TTL. If expired, TTL is restarted.
func (c *CheckTTL) SetStatus(status, output string) { // output is returned (might be truncated)
func (c *CheckTTL) SetStatus(status, output string) string {
c.Logger.Printf("[DEBUG] agent: Check %q status is now %s", c.CheckID, status) c.Logger.Printf("[DEBUG] agent: Check %q status is now %s", c.CheckID, status)
total := len(output)
if total > c.OutputMaxSize {
output = fmt.Sprintf("%s ... (captured %d of %d bytes)",
output[:c.OutputMaxSize], c.OutputMaxSize, total)
}
c.Notify.UpdateCheck(c.CheckID, status, output) c.Notify.UpdateCheck(c.CheckID, status, output)
// Store the last output so we can retain it if the TTL expires. // Store the last output so we can retain it if the TTL expires.
c.lastOutputLock.Lock() c.lastOutputLock.Lock()
c.lastOutput = output c.lastOutput = output
c.lastOutputLock.Unlock() c.lastOutputLock.Unlock()
c.timer.Reset(c.TTL) c.timer.Reset(c.TTL)
return output
} }
// CheckHTTP is used to periodically make an HTTP request to // CheckHTTP is used to periodically make an HTTP request to
@ -303,6 +315,7 @@ type CheckHTTP struct {
Timeout time.Duration Timeout time.Duration
Logger *log.Logger Logger *log.Logger
TLSClientConfig *tls.Config TLSClientConfig *tls.Config
OutputMaxSize int
httpClient *http.Client httpClient *http.Client
stop bool stop bool
@ -339,6 +352,9 @@ func (c *CheckHTTP) Start() {
} else if c.Interval < 10*time.Second { } else if c.Interval < 10*time.Second {
c.httpClient.Timeout = c.Interval c.httpClient.Timeout = c.Interval
} }
if c.OutputMaxSize < 1 {
c.OutputMaxSize = DefaultBufSize
}
} }
c.stop = false c.stop = false
@ -413,7 +429,7 @@ func (c *CheckHTTP) check() {
defer resp.Body.Close() defer resp.Body.Close()
// Read the response into a circular buffer to limit the size // Read the response into a circular buffer to limit the size
output, _ := circbuf.NewBuffer(BufSize) output, _ := circbuf.NewBuffer(int64(c.OutputMaxSize))
if _, err := io.Copy(output, resp.Body); err != nil { if _, err := io.Copy(output, resp.Body); err != nil {
c.Logger.Printf("[WARN] agent: Check %q error while reading body: %s", c.CheckID, err) c.Logger.Printf("[WARN] agent: Check %q error while reading body: %s", c.CheckID, err)
} }

View File

@ -44,11 +44,12 @@ func TestCheckMonitor_Script(t *testing.T) {
t.Run(tt.status, func(t *testing.T) { t.Run(tt.status, func(t *testing.T) {
notif := mock.NewNotify() notif := mock.NewNotify()
check := &CheckMonitor{ check := &CheckMonitor{
Notify: notif, Notify: notif,
CheckID: types.CheckID("foo"), CheckID: types.CheckID("foo"),
Script: tt.script, Script: tt.script,
Interval: 25 * time.Millisecond, Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags), Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
} }
check.Start() check.Start()
defer check.Stop() defer check.Stop()
@ -79,11 +80,12 @@ func TestCheckMonitor_Args(t *testing.T) {
t.Run(tt.status, func(t *testing.T) { t.Run(tt.status, func(t *testing.T) {
notif := mock.NewNotify() notif := mock.NewNotify()
check := &CheckMonitor{ check := &CheckMonitor{
Notify: notif, Notify: notif,
CheckID: types.CheckID("foo"), CheckID: types.CheckID("foo"),
ScriptArgs: tt.args, ScriptArgs: tt.args,
Interval: 25 * time.Millisecond, Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags), Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
} }
check.Start() check.Start()
defer check.Stop() defer check.Stop()
@ -103,12 +105,13 @@ func TestCheckMonitor_Timeout(t *testing.T) {
// t.Parallel() // timing test. no parallel // t.Parallel() // timing test. no parallel
notif := mock.NewNotify() notif := mock.NewNotify()
check := &CheckMonitor{ check := &CheckMonitor{
Notify: notif, Notify: notif,
CheckID: types.CheckID("foo"), CheckID: types.CheckID("foo"),
ScriptArgs: []string{"sh", "-c", "sleep 1 && exit 0"}, ScriptArgs: []string{"sh", "-c", "sleep 1 && exit 0"},
Interval: 50 * time.Millisecond, Interval: 50 * time.Millisecond,
Timeout: 25 * time.Millisecond, Timeout: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags), Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
} }
check.Start() check.Start()
defer check.Stop() defer check.Stop()
@ -128,11 +131,12 @@ func TestCheckMonitor_RandomStagger(t *testing.T) {
// t.Parallel() // timing test. no parallel // t.Parallel() // timing test. no parallel
notif := mock.NewNotify() notif := mock.NewNotify()
check := &CheckMonitor{ check := &CheckMonitor{
Notify: notif, Notify: notif,
CheckID: types.CheckID("foo"), CheckID: types.CheckID("foo"),
ScriptArgs: []string{"sh", "-c", "exit 0"}, ScriptArgs: []string{"sh", "-c", "exit 0"},
Interval: 25 * time.Millisecond, Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags), Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
} }
check.Start() check.Start()
defer check.Stop() defer check.Stop()
@ -153,11 +157,12 @@ func TestCheckMonitor_LimitOutput(t *testing.T) {
t.Parallel() t.Parallel()
notif := mock.NewNotify() notif := mock.NewNotify()
check := &CheckMonitor{ check := &CheckMonitor{
Notify: notif, Notify: notif,
CheckID: types.CheckID("foo"), CheckID: types.CheckID("foo"),
ScriptArgs: []string{"od", "-N", "81920", "/dev/urandom"}, ScriptArgs: []string{"od", "-N", "81920", "/dev/urandom"},
Interval: 25 * time.Millisecond, Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags), Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
} }
check.Start() check.Start()
defer check.Stop() defer check.Stop()
@ -165,7 +170,7 @@ func TestCheckMonitor_LimitOutput(t *testing.T) {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
// Allow for extra bytes for the truncation message // Allow for extra bytes for the truncation message
if len(notif.Output("foo")) > BufSize+100 { if len(notif.Output("foo")) > DefaultBufSize+100 {
t.Fatalf("output size is too long") t.Fatalf("output size is too long")
} }
} }
@ -287,7 +292,7 @@ func TestCheckHTTP(t *testing.T) {
} }
// Body larger than 4k limit // Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*BufSize) body := bytes.Repeat([]byte{'a'}, 2*DefaultBufSize)
w.WriteHeader(tt.code) w.WriteHeader(tt.code)
w.Write(body) w.Write(body)
})) }))
@ -295,13 +300,14 @@ func TestCheckHTTP(t *testing.T) {
notif := mock.NewNotify() notif := mock.NewNotify()
check := &CheckHTTP{ check := &CheckHTTP{
Notify: notif, Notify: notif,
CheckID: types.CheckID("foo"), CheckID: types.CheckID("foo"),
HTTP: server.URL, HTTP: server.URL,
Method: tt.method, Method: tt.method,
Header: tt.header, OutputMaxSize: DefaultBufSize,
Interval: 10 * time.Millisecond, Header: tt.header,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags), Interval: 10 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
} }
check.Start() check.Start()
defer check.Stop() defer check.Stop()
@ -313,15 +319,52 @@ func TestCheckHTTP(t *testing.T) {
if got, want := notif.State("foo"), tt.status; got != want { if got, want := notif.State("foo"), tt.status; got != want {
r.Fatalf("got state %q want %q", got, want) r.Fatalf("got state %q want %q", got, want)
} }
// Allow slightly more data than BufSize, for the header // Allow slightly more data than DefaultBufSize, for the header
if n := len(notif.Output("foo")); n > (BufSize + 256) { if n := len(notif.Output("foo")); n > (DefaultBufSize + 256) {
r.Fatalf("output too long: %d (%d-byte limit)", n, BufSize) r.Fatalf("output too long: %d (%d-byte limit)", n, DefaultBufSize)
} }
}) })
}) })
} }
} }
func TestCheckMaxOutputSize(t *testing.T) {
t.Parallel()
timeout := 5 * time.Millisecond
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) {
body := bytes.Repeat([]byte{'x'}, 2*DefaultBufSize)
writer.WriteHeader(200)
writer.Write(body)
}))
defer server.Close()
notif := mock.NewNotify()
maxOutputSize := 32
check := &CheckHTTP{
Notify: notif,
CheckID: types.CheckID("bar"),
HTTP: server.URL + "/v1/agent/self",
Timeout: timeout,
Interval: 2 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: maxOutputSize,
}
check.Start()
defer check.Stop()
retry.Run(t, func(r *retry.R) {
if got, want := notif.Updates("bar"), 2; got < want {
r.Fatalf("got %d updates want at least %d", got, want)
}
if got, want := notif.State("bar"), api.HealthPassing; got != want {
r.Fatalf("got state %q want %q", got, want)
}
if got, want := notif.Output("bar"), "HTTP GET "+server.URL+"/v1/agent/self: 200 OK Output: "+strings.Repeat("x", maxOutputSize); got != want {
r.Fatalf("got state %q want %q", got, want)
}
})
}
func TestCheckHTTPTimeout(t *testing.T) { func TestCheckHTTPTimeout(t *testing.T) {
t.Parallel() t.Parallel()
timeout := 5 * time.Millisecond timeout := 5 * time.Millisecond
@ -372,7 +415,7 @@ func TestCheckHTTP_disablesKeepAlives(t *testing.T) {
func largeBodyHandler(code int) http.Handler { func largeBodyHandler(code int) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Body larger than 4k limit // Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*BufSize) body := bytes.Repeat([]byte{'a'}, 2*DefaultBufSize)
w.WriteHeader(code) w.WriteHeader(code)
w.Write(body) w.Write(body)
}) })

View File

@ -14,6 +14,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/connect/ca" "github.com/hashicorp/consul/agent/connect/ca"
"github.com/hashicorp/consul/agent/consul" "github.com/hashicorp/consul/agent/consul"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
@ -786,6 +787,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
CAPath: b.stringVal(c.CAPath), CAPath: b.stringVal(c.CAPath),
CertFile: b.stringVal(c.CertFile), CertFile: b.stringVal(c.CertFile),
CheckUpdateInterval: b.durationVal("check_update_interval", c.CheckUpdateInterval), CheckUpdateInterval: b.durationVal("check_update_interval", c.CheckUpdateInterval),
CheckOutputMaxSize: b.intValWithDefault(c.CheckOutputMaxSize, 4096),
Checks: checks, Checks: checks,
ClientAddrs: clientAddrs, ClientAddrs: clientAddrs,
ConfigEntryBootstrap: configEntries, ConfigEntryBootstrap: configEntries,
@ -880,6 +882,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
TaggedAddresses: c.TaggedAddresses, TaggedAddresses: c.TaggedAddresses,
TranslateWANAddrs: b.boolVal(c.TranslateWANAddrs), TranslateWANAddrs: b.boolVal(c.TranslateWANAddrs),
UIDir: b.stringVal(c.UIDir), UIDir: b.stringVal(c.UIDir),
UIContentPath: UIPathBuilder(b.stringVal(b.Flags.Config.UIContentPath)),
UnixSocketGroup: b.stringVal(c.UnixSocket.Group), UnixSocketGroup: b.stringVal(c.UnixSocket.Group),
UnixSocketMode: b.stringVal(c.UnixSocket.Mode), UnixSocketMode: b.stringVal(c.UnixSocket.Mode),
UnixSocketUser: b.stringVal(c.UnixSocket.User), UnixSocketUser: b.stringVal(c.UnixSocket.User),
@ -905,6 +908,9 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
// reDatacenter defines a regexp for a valid datacenter name // reDatacenter defines a regexp for a valid datacenter name
var reDatacenter = regexp.MustCompile("^[a-z0-9_-]+$") var reDatacenter = regexp.MustCompile("^[a-z0-9_-]+$")
// validContentPath defines a regexp for a valid content path name.
var validContentPath = regexp.MustCompile(`^[A-Za-z0-9/_-]+$`)
var hasVersion = regexp.MustCompile(`^/v\d+/$`)
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// check required params we cannot recover from first // check required params we cannot recover from first
// //
@ -913,11 +919,20 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
return fmt.Errorf("datacenter cannot be empty") return fmt.Errorf("datacenter cannot be empty")
} }
if !reDatacenter.MatchString(rt.Datacenter) { if !reDatacenter.MatchString(rt.Datacenter) {
return fmt.Errorf("datacenter cannot be %q. Please use only [a-z0-9-_].", rt.Datacenter) return fmt.Errorf("datacenter cannot be %q. Please use only [a-z0-9-_]", rt.Datacenter)
} }
if rt.DataDir == "" && !rt.DevMode { if rt.DataDir == "" && !rt.DevMode {
return fmt.Errorf("data_dir cannot be empty") return fmt.Errorf("data_dir cannot be empty")
} }
if !validContentPath.MatchString(rt.UIContentPath) {
return fmt.Errorf("ui-content-path can only contain alphanumeric, -, _, or /. received: %s", rt.UIContentPath)
}
if hasVersion.MatchString(rt.UIContentPath) {
return fmt.Errorf("ui-content-path cannot have 'v[0-9]'. received: %s", rt.UIContentPath)
}
if !rt.DevMode { if !rt.DevMode {
fi, err := os.Stat(rt.DataDir) fi, err := os.Stat(rt.DataDir)
switch { switch {
@ -964,6 +979,9 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
if rt.BootstrapExpect > 0 && rt.Bootstrap { if rt.BootstrapExpect > 0 && rt.Bootstrap {
return fmt.Errorf("'bootstrap_expect > 0' and 'bootstrap = true' are mutually exclusive") return fmt.Errorf("'bootstrap_expect > 0' and 'bootstrap = true' are mutually exclusive")
} }
if rt.CheckOutputMaxSize < 1 {
return fmt.Errorf("check_output_max_size must be positive, to discard check output use the discard_check_output flag")
}
if rt.AEInterval <= 0 { if rt.AEInterval <= 0 {
return fmt.Errorf("ae_interval cannot be %s. Must be positive", rt.AEInterval) return fmt.Errorf("ae_interval cannot be %s. Must be positive", rt.AEInterval)
} }
@ -971,7 +989,7 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
return fmt.Errorf("autopilot.max_trailing_logs cannot be %d. Must be greater than or equal to zero", rt.AutopilotMaxTrailingLogs) return fmt.Errorf("autopilot.max_trailing_logs cannot be %d. Must be greater than or equal to zero", rt.AutopilotMaxTrailingLogs)
} }
if rt.ACLDatacenter != "" && !reDatacenter.MatchString(rt.ACLDatacenter) { if rt.ACLDatacenter != "" && !reDatacenter.MatchString(rt.ACLDatacenter) {
return fmt.Errorf("acl_datacenter cannot be %q. Please use only [a-z0-9-_].", rt.ACLDatacenter) return fmt.Errorf("acl_datacenter cannot be %q. Please use only [a-z0-9-_]", rt.ACLDatacenter)
} }
if rt.EnableUI && rt.UIDir != "" { if rt.EnableUI && rt.UIDir != "" {
return fmt.Errorf( return fmt.Errorf(
@ -1174,6 +1192,7 @@ func (b *Builder) checkVal(v *CheckDefinition) *structs.CheckDefinition {
Timeout: b.durationVal(fmt.Sprintf("check[%s].timeout", id), v.Timeout), Timeout: b.durationVal(fmt.Sprintf("check[%s].timeout", id), v.Timeout),
TTL: b.durationVal(fmt.Sprintf("check[%s].ttl", id), v.TTL), TTL: b.durationVal(fmt.Sprintf("check[%s].ttl", id), v.TTL),
DeregisterCriticalServiceAfter: b.durationVal(fmt.Sprintf("check[%s].deregister_critical_service_after", id), v.DeregisterCriticalServiceAfter), DeregisterCriticalServiceAfter: b.durationVal(fmt.Sprintf("check[%s].deregister_critical_service_after", id), v.DeregisterCriticalServiceAfter),
OutputMaxSize: b.intValWithDefault(v.OutputMaxSize, checks.DefaultBufSize),
} }
} }
@ -1368,13 +1387,17 @@ func (b *Builder) durationVal(name string, v *string) (d time.Duration) {
return b.durationValWithDefault(name, v, 0) return b.durationValWithDefault(name, v, 0)
} }
func (b *Builder) intVal(v *int) int { func (b *Builder) intValWithDefault(v *int, defaultVal int) int {
if v == nil { if v == nil {
return 0 return defaultVal
} }
return *v return *v
} }
func (b *Builder) intVal(v *int) int {
return b.intValWithDefault(v, 0)
}
func (b *Builder) portVal(name string, v *int) int { func (b *Builder) portVal(name string, v *int) int {
if v == nil || *v <= 0 { if v == nil || *v <= 0 {
return -1 return -1
@ -1683,3 +1706,17 @@ func isUnixAddr(a net.Addr) bool {
_, ok := a.(*net.UnixAddr) _, ok := a.(*net.UnixAddr)
return ok return ok
} }
// UIPathBuilder checks to see if there was a path set
// If so, adds beginning and trailing slashes to UI path
func UIPathBuilder(UIContentString string) string {
if UIContentString != "" {
var fmtedPath string
fmtedPath = strings.Trim(UIContentString, "/")
fmtedPath = "/" + fmtedPath + "/"
return fmtedPath
}
return "/ui/"
}

View File

@ -184,6 +184,7 @@ type Config struct {
CAPath *string `json:"ca_path,omitempty" hcl:"ca_path" mapstructure:"ca_path"` CAPath *string `json:"ca_path,omitempty" hcl:"ca_path" mapstructure:"ca_path"`
CertFile *string `json:"cert_file,omitempty" hcl:"cert_file" mapstructure:"cert_file"` CertFile *string `json:"cert_file,omitempty" hcl:"cert_file" mapstructure:"cert_file"`
Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"` // needs to be a pointer to avoid partial merges Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"` // needs to be a pointer to avoid partial merges
CheckOutputMaxSize *int `json:"check_output_max_size,omitempty" hcl:"check_output_max_size" mapstructure:"check_output_max_size"`
CheckUpdateInterval *string `json:"check_update_interval,omitempty" hcl:"check_update_interval" mapstructure:"check_update_interval"` CheckUpdateInterval *string `json:"check_update_interval,omitempty" hcl:"check_update_interval" mapstructure:"check_update_interval"`
Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"` Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
ClientAddr *string `json:"client_addr,omitempty" hcl:"client_addr" mapstructure:"client_addr"` ClientAddr *string `json:"client_addr,omitempty" hcl:"client_addr" mapstructure:"client_addr"`
@ -264,6 +265,7 @@ type Config struct {
Telemetry Telemetry `json:"telemetry,omitempty" hcl:"telemetry" mapstructure:"telemetry"` Telemetry Telemetry `json:"telemetry,omitempty" hcl:"telemetry" mapstructure:"telemetry"`
TranslateWANAddrs *bool `json:"translate_wan_addrs,omitempty" hcl:"translate_wan_addrs" mapstructure:"translate_wan_addrs"` TranslateWANAddrs *bool `json:"translate_wan_addrs,omitempty" hcl:"translate_wan_addrs" mapstructure:"translate_wan_addrs"`
UI *bool `json:"ui,omitempty" hcl:"ui" mapstructure:"ui"` UI *bool `json:"ui,omitempty" hcl:"ui" mapstructure:"ui"`
UIContentPath *string `json:"ui_content_path,omitempty" hcl:"ui_content_path" mapstructure:"ui_content_path"`
UIDir *string `json:"ui_dir,omitempty" hcl:"ui_dir" mapstructure:"ui_dir"` UIDir *string `json:"ui_dir,omitempty" hcl:"ui_dir" mapstructure:"ui_dir"`
UnixSocket UnixSocket `json:"unix_sockets,omitempty" hcl:"unix_sockets" mapstructure:"unix_sockets"` UnixSocket UnixSocket `json:"unix_sockets,omitempty" hcl:"unix_sockets" mapstructure:"unix_sockets"`
VerifyIncoming *bool `json:"verify_incoming,omitempty" hcl:"verify_incoming" mapstructure:"verify_incoming"` VerifyIncoming *bool `json:"verify_incoming,omitempty" hcl:"verify_incoming" mapstructure:"verify_incoming"`
@ -396,6 +398,7 @@ type CheckDefinition struct {
HTTP *string `json:"http,omitempty" hcl:"http" mapstructure:"http"` HTTP *string `json:"http,omitempty" hcl:"http" mapstructure:"http"`
Header map[string][]string `json:"header,omitempty" hcl:"header" mapstructure:"header"` Header map[string][]string `json:"header,omitempty" hcl:"header" mapstructure:"header"`
Method *string `json:"method,omitempty" hcl:"method" mapstructure:"method"` Method *string `json:"method,omitempty" hcl:"method" mapstructure:"method"`
OutputMaxSize *int `json:"output_max_size,omitempty" hcl:"output_max_size" mapstructure:"output_max_size"`
TCP *string `json:"tcp,omitempty" hcl:"tcp" mapstructure:"tcp"` TCP *string `json:"tcp,omitempty" hcl:"tcp" mapstructure:"tcp"`
Interval *string `json:"interval,omitempty" hcl:"interval" mapstructure:"interval"` Interval *string `json:"interval,omitempty" hcl:"interval" mapstructure:"interval"`
DockerContainerID *string `json:"docker_container_id,omitempty" hcl:"docker_container_id" mapstructure:"docker_container_id"` DockerContainerID *string `json:"docker_container_id,omitempty" hcl:"docker_container_id" mapstructure:"docker_container_id"`

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/consul" "github.com/hashicorp/consul/agent/consul"
"github.com/hashicorp/consul/version" "github.com/hashicorp/consul/version"
) )
@ -49,6 +50,7 @@ func DefaultSource() Source {
bind_addr = "0.0.0.0" bind_addr = "0.0.0.0"
bootstrap = false bootstrap = false
bootstrap_expect = 0 bootstrap_expect = 0
check_output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
check_update_interval = "5m" check_update_interval = "5m"
client_addr = "127.0.0.1" client_addr = "127.0.0.1"
datacenter = "` + consul.DefaultDC + `" datacenter = "` + consul.DefaultDC + `"

View File

@ -60,6 +60,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
add(&f.Config.Bootstrap, "bootstrap", "Sets server to bootstrap mode.") add(&f.Config.Bootstrap, "bootstrap", "Sets server to bootstrap mode.")
add(&f.Config.BootstrapExpect, "bootstrap-expect", "Sets server to expect bootstrap mode.") add(&f.Config.BootstrapExpect, "bootstrap-expect", "Sets server to expect bootstrap mode.")
add(&f.Config.ClientAddr, "client", "Sets the address to bind for client access. This includes RPC, DNS, HTTP, HTTPS and gRPC (if configured).") add(&f.Config.ClientAddr, "client", "Sets the address to bind for client access. This includes RPC, DNS, HTTP, HTTPS and gRPC (if configured).")
add(&f.Config.CheckOutputMaxSize, "check_output_max_size", "Sets the maximum output size for checks on this agent")
add(&f.ConfigFiles, "config-dir", "Path to a directory to read configuration files from. This will read every file ending in '.json' as configuration in this directory in alphabetical order. Can be specified multiple times.") add(&f.ConfigFiles, "config-dir", "Path to a directory to read configuration files from. This will read every file ending in '.json' as configuration in this directory in alphabetical order. Can be specified multiple times.")
add(&f.ConfigFiles, "config-file", "Path to a file in JSON or HCL format with a matching file extension. Can be specified multiple times.") add(&f.ConfigFiles, "config-file", "Path to a file in JSON or HCL format with a matching file extension. Can be specified multiple times.")
add(&f.ConfigFormat, "config-format", "Config files are in this format irrespective of their extension. Must be 'hcl' or 'json'") add(&f.ConfigFormat, "config-format", "Config files are in this format irrespective of their extension. Must be 'hcl' or 'json'")
@ -105,6 +106,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
add(&f.Config.ServerMode, "server", "Switches agent to server mode.") add(&f.Config.ServerMode, "server", "Switches agent to server mode.")
add(&f.Config.EnableSyslog, "syslog", "Enables logging to syslog.") add(&f.Config.EnableSyslog, "syslog", "Enables logging to syslog.")
add(&f.Config.UI, "ui", "Enables the built-in static web UI server.") add(&f.Config.UI, "ui", "Enables the built-in static web UI server.")
add(&f.Config.UIContentPath, "ui-content-path", "Sets the external UI path to a string. Defaults to: /ui/ ")
add(&f.Config.UIDir, "ui-dir", "Path to directory containing the web UI resources.") add(&f.Config.UIDir, "ui-dir", "Path to directory containing the web UI resources.")
add(&f.HCL, "hcl", "hcl config fragment. Can be specified multiple times.") add(&f.HCL, "hcl", "hcl config fragment. Can be specified multiple times.")
} }

View File

@ -459,6 +459,11 @@ type RuntimeConfig struct {
// hcl: check_update_interval = "duration" // hcl: check_update_interval = "duration"
CheckUpdateInterval time.Duration CheckUpdateInterval time.Duration
// Maximum size for the output of a healtcheck
// hcl check_output_max_size int
// flag: -check_output_max_size int
CheckOutputMaxSize int
// Checks contains the provided check definitions. // Checks contains the provided check definitions.
// //
// hcl: checks = [ // hcl: checks = [
@ -1382,6 +1387,10 @@ type RuntimeConfig struct {
// flag: -ui-dir string // flag: -ui-dir string
UIDir string UIDir string
//UIContentPath is a string that sets the external
// path to a string. Default: /ui/
UIContentPath string
// UnixSocketGroup contains the group of the file permissions when // UnixSocketGroup contains the group of the file permissions when
// Consul binds to UNIX sockets. // Consul binds to UNIX sockets.
// //

View File

@ -18,6 +18,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil"
@ -742,6 +743,18 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.DataDir = dataDir rt.DataDir = dataDir
}, },
}, },
{
desc: "-ui-content-path",
args: []string{
`-ui-content-path=/a/b`,
`-data-dir=` + dataDir,
},
patch: func(rt *RuntimeConfig) {
rt.UIContentPath = "/a/b/"
rt.DataDir = dataDir
},
},
// ------------------------------------------------------------ // ------------------------------------------------------------
// ports and addresses // ports and addresses
@ -2076,8 +2089,8 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
}, },
patch: func(rt *RuntimeConfig) { patch: func(rt *RuntimeConfig) {
rt.Checks = []*structs.CheckDefinition{ rt.Checks = []*structs.CheckDefinition{
&structs.CheckDefinition{Name: "a", ScriptArgs: []string{"/bin/true"}}, &structs.CheckDefinition{Name: "a", ScriptArgs: []string{"/bin/true"}, OutputMaxSize: checks.DefaultBufSize},
&structs.CheckDefinition{Name: "b", ScriptArgs: []string{"/bin/false"}}, &structs.CheckDefinition{Name: "b", ScriptArgs: []string{"/bin/false"}, OutputMaxSize: checks.DefaultBufSize},
} }
rt.DataDir = dataDir rt.DataDir = dataDir
}, },
@ -2095,7 +2108,7 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
}, },
patch: func(rt *RuntimeConfig) { patch: func(rt *RuntimeConfig) {
rt.Checks = []*structs.CheckDefinition{ rt.Checks = []*structs.CheckDefinition{
&structs.CheckDefinition{Name: "a", GRPC: "localhost:12345/foo", GRPCUseTLS: true}, &structs.CheckDefinition{Name: "a", GRPC: "localhost:12345/foo", GRPCUseTLS: true, OutputMaxSize: checks.DefaultBufSize},
} }
rt.DataDir = dataDir rt.DataDir = dataDir
}, },
@ -2113,7 +2126,7 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
}, },
patch: func(rt *RuntimeConfig) { patch: func(rt *RuntimeConfig) {
rt.Checks = []*structs.CheckDefinition{ rt.Checks = []*structs.CheckDefinition{
&structs.CheckDefinition{Name: "a", AliasService: "foo"}, &structs.CheckDefinition{Name: "a", AliasService: "foo", OutputMaxSize: checks.DefaultBufSize},
} }
rt.DataDir = dataDir rt.DataDir = dataDir
}, },
@ -2250,6 +2263,7 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
DockerContainerID: "z", DockerContainerID: "z",
DeregisterCriticalServiceAfter: 10 * time.Second, DeregisterCriticalServiceAfter: 10 * time.Second,
ScriptArgs: []string{"a", "b"}, ScriptArgs: []string{"a", "b"},
OutputMaxSize: checks.DefaultBufSize,
}, },
}, },
Weights: &structs.Weights{ Weights: &structs.Weights{
@ -2515,8 +2529,9 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
Port: 2345, Port: 2345,
Checks: structs.CheckTypes{ Checks: structs.CheckTypes{
{ {
TCP: "127.0.0.1:2345", TCP: "127.0.0.1:2345",
Interval: 10 * time.Second, Interval: 10 * time.Second,
OutputMaxSize: checks.DefaultBufSize,
}, },
}, },
Proxy: &structs.ConnectProxyConfig{ Proxy: &structs.ConnectProxyConfig{
@ -2610,8 +2625,9 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
Port: 2345, Port: 2345,
Checks: structs.CheckTypes{ Checks: structs.CheckTypes{
{ {
TCP: "127.0.0.1:2345", TCP: "127.0.0.1:2345",
Interval: 10 * time.Second, Interval: 10 * time.Second,
OutputMaxSize: checks.DefaultBufSize,
}, },
}, },
Proxy: &structs.ConnectProxyConfig{ Proxy: &structs.ConnectProxyConfig{
@ -3043,6 +3059,7 @@ func TestFullConfig(t *testing.T) {
"f3r6xFtM": [ "RyuIdDWv", "QbxEcIUM" ] "f3r6xFtM": [ "RyuIdDWv", "QbxEcIUM" ]
}, },
"method": "Dou0nGT5", "method": "Dou0nGT5",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"tcp": "JY6fTTcw", "tcp": "JY6fTTcw",
"interval": "18714s", "interval": "18714s",
"docker_container_id": "qF66POS9", "docker_container_id": "qF66POS9",
@ -3069,6 +3086,7 @@ func TestFullConfig(t *testing.T) {
"method": "aldrIQ4l", "method": "aldrIQ4l",
"tcp": "RJQND605", "tcp": "RJQND605",
"interval": "22164s", "interval": "22164s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "ipgdFtjd", "docker_container_id": "ipgdFtjd",
"shell": "qAeOYy0M", "shell": "qAeOYy0M",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3092,6 +3110,7 @@ func TestFullConfig(t *testing.T) {
"method": "gLrztrNw", "method": "gLrztrNw",
"tcp": "4jG5casb", "tcp": "4jG5casb",
"interval": "28767s", "interval": "28767s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "THW6u7rL", "docker_container_id": "THW6u7rL",
"shell": "C1Zt3Zwh", "shell": "C1Zt3Zwh",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3302,6 +3321,7 @@ func TestFullConfig(t *testing.T) {
"method": "9afLm3Mj", "method": "9afLm3Mj",
"tcp": "fjiLFqVd", "tcp": "fjiLFqVd",
"interval": "23926s", "interval": "23926s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "dO5TtRHk", "docker_container_id": "dO5TtRHk",
"shell": "e6q2ttES", "shell": "e6q2ttES",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3324,6 +3344,7 @@ func TestFullConfig(t *testing.T) {
"method": "T66MFBfR", "method": "T66MFBfR",
"tcp": "bNnNfx2A", "tcp": "bNnNfx2A",
"interval": "22224s", "interval": "22224s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "ipgdFtjd", "docker_container_id": "ipgdFtjd",
"shell": "omVZq7Sz", "shell": "omVZq7Sz",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3345,6 +3366,7 @@ func TestFullConfig(t *testing.T) {
"method": "ciYHWors", "method": "ciYHWors",
"tcp": "FfvCwlqH", "tcp": "FfvCwlqH",
"interval": "12356s", "interval": "12356s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "HBndBU6R", "docker_container_id": "HBndBU6R",
"shell": "hVI33JjA", "shell": "hVI33JjA",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3380,6 +3402,7 @@ func TestFullConfig(t *testing.T) {
"method": "X5DrovFc", "method": "X5DrovFc",
"tcp": "ICbxkpSF", "tcp": "ICbxkpSF",
"interval": "24392s", "interval": "24392s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "ZKXr68Yb", "docker_container_id": "ZKXr68Yb",
"shell": "CEfzx0Fo", "shell": "CEfzx0Fo",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3418,6 +3441,7 @@ func TestFullConfig(t *testing.T) {
"method": "5wkAxCUE", "method": "5wkAxCUE",
"tcp": "MN3oA9D2", "tcp": "MN3oA9D2",
"interval": "32718s", "interval": "32718s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "cU15LMet", "docker_container_id": "cU15LMet",
"shell": "nEz9qz2l", "shell": "nEz9qz2l",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3439,6 +3463,7 @@ func TestFullConfig(t *testing.T) {
"method": "wzByP903", "method": "wzByP903",
"tcp": "2exjZIGE", "tcp": "2exjZIGE",
"interval": "5656s", "interval": "5656s",
"output_max_size": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"docker_container_id": "5tDBWpfA", "docker_container_id": "5tDBWpfA",
"shell": "rlTpLM8s", "shell": "rlTpLM8s",
"tls_skip_verify": true, "tls_skip_verify": true,
@ -3621,6 +3646,7 @@ func TestFullConfig(t *testing.T) {
method = "Dou0nGT5" method = "Dou0nGT5"
tcp = "JY6fTTcw" tcp = "JY6fTTcw"
interval = "18714s" interval = "18714s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "qF66POS9" docker_container_id = "qF66POS9"
shell = "sOnDy228" shell = "sOnDy228"
tls_skip_verify = true tls_skip_verify = true
@ -3645,6 +3671,7 @@ func TestFullConfig(t *testing.T) {
method = "aldrIQ4l" method = "aldrIQ4l"
tcp = "RJQND605" tcp = "RJQND605"
interval = "22164s" interval = "22164s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "ipgdFtjd" docker_container_id = "ipgdFtjd"
shell = "qAeOYy0M" shell = "qAeOYy0M"
tls_skip_verify = true tls_skip_verify = true
@ -3668,6 +3695,7 @@ func TestFullConfig(t *testing.T) {
method = "gLrztrNw" method = "gLrztrNw"
tcp = "4jG5casb" tcp = "4jG5casb"
interval = "28767s" interval = "28767s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "THW6u7rL" docker_container_id = "THW6u7rL"
shell = "C1Zt3Zwh" shell = "C1Zt3Zwh"
tls_skip_verify = true tls_skip_verify = true
@ -3903,6 +3931,7 @@ func TestFullConfig(t *testing.T) {
method = "T66MFBfR" method = "T66MFBfR"
tcp = "bNnNfx2A" tcp = "bNnNfx2A"
interval = "22224s" interval = "22224s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "ipgdFtjd" docker_container_id = "ipgdFtjd"
shell = "omVZq7Sz" shell = "omVZq7Sz"
tls_skip_verify = true tls_skip_verify = true
@ -3924,6 +3953,7 @@ func TestFullConfig(t *testing.T) {
method = "ciYHWors" method = "ciYHWors"
tcp = "FfvCwlqH" tcp = "FfvCwlqH"
interval = "12356s" interval = "12356s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "HBndBU6R" docker_container_id = "HBndBU6R"
shell = "hVI33JjA" shell = "hVI33JjA"
tls_skip_verify = true tls_skip_verify = true
@ -3959,6 +3989,7 @@ func TestFullConfig(t *testing.T) {
method = "X5DrovFc" method = "X5DrovFc"
tcp = "ICbxkpSF" tcp = "ICbxkpSF"
interval = "24392s" interval = "24392s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "ZKXr68Yb" docker_container_id = "ZKXr68Yb"
shell = "CEfzx0Fo" shell = "CEfzx0Fo"
tls_skip_verify = true tls_skip_verify = true
@ -3997,6 +4028,7 @@ func TestFullConfig(t *testing.T) {
method = "5wkAxCUE" method = "5wkAxCUE"
tcp = "MN3oA9D2" tcp = "MN3oA9D2"
interval = "32718s" interval = "32718s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "cU15LMet" docker_container_id = "cU15LMet"
shell = "nEz9qz2l" shell = "nEz9qz2l"
tls_skip_verify = true tls_skip_verify = true
@ -4018,6 +4050,7 @@ func TestFullConfig(t *testing.T) {
method = "wzByP903" method = "wzByP903"
tcp = "2exjZIGE" tcp = "2exjZIGE"
interval = "5656s" interval = "5656s"
output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
docker_container_id = "5tDBWpfA" docker_container_id = "5tDBWpfA"
shell = "rlTpLM8s" shell = "rlTpLM8s"
tls_skip_verify = true tls_skip_verify = true
@ -4286,6 +4319,7 @@ func TestFullConfig(t *testing.T) {
CAFile: "erA7T0PM", CAFile: "erA7T0PM",
CAPath: "mQEN1Mfp", CAPath: "mQEN1Mfp",
CertFile: "7s4QAzDk", CertFile: "7s4QAzDk",
CheckOutputMaxSize: checks.DefaultBufSize,
Checks: []*structs.CheckDefinition{ Checks: []*structs.CheckDefinition{
&structs.CheckDefinition{ &structs.CheckDefinition{
ID: "uAjE6m9Z", ID: "uAjE6m9Z",
@ -4303,6 +4337,7 @@ func TestFullConfig(t *testing.T) {
Method: "aldrIQ4l", Method: "aldrIQ4l",
TCP: "RJQND605", TCP: "RJQND605",
Interval: 22164 * time.Second, Interval: 22164 * time.Second,
OutputMaxSize: checks.DefaultBufSize,
DockerContainerID: "ipgdFtjd", DockerContainerID: "ipgdFtjd",
Shell: "qAeOYy0M", Shell: "qAeOYy0M",
TLSSkipVerify: true, TLSSkipVerify: true,
@ -4324,6 +4359,7 @@ func TestFullConfig(t *testing.T) {
"qxvdnSE9": []string{"6wBPUYdF", "YYh8wtSZ"}, "qxvdnSE9": []string{"6wBPUYdF", "YYh8wtSZ"},
}, },
Method: "gLrztrNw", Method: "gLrztrNw",
OutputMaxSize: checks.DefaultBufSize,
TCP: "4jG5casb", TCP: "4jG5casb",
Interval: 28767 * time.Second, Interval: 28767 * time.Second,
DockerContainerID: "THW6u7rL", DockerContainerID: "THW6u7rL",
@ -4347,6 +4383,7 @@ func TestFullConfig(t *testing.T) {
"f3r6xFtM": {"RyuIdDWv", "QbxEcIUM"}, "f3r6xFtM": {"RyuIdDWv", "QbxEcIUM"},
}, },
Method: "Dou0nGT5", Method: "Dou0nGT5",
OutputMaxSize: checks.DefaultBufSize,
TCP: "JY6fTTcw", TCP: "JY6fTTcw",
Interval: 18714 * time.Second, Interval: 18714 * time.Second,
DockerContainerID: "qF66POS9", DockerContainerID: "qF66POS9",
@ -4515,6 +4552,7 @@ func TestFullConfig(t *testing.T) {
"cVFpko4u": {"gGqdEB6k", "9LsRo22u"}, "cVFpko4u": {"gGqdEB6k", "9LsRo22u"},
}, },
Method: "X5DrovFc", Method: "X5DrovFc",
OutputMaxSize: checks.DefaultBufSize,
TCP: "ICbxkpSF", TCP: "ICbxkpSF",
Interval: 24392 * time.Second, Interval: 24392 * time.Second,
DockerContainerID: "ZKXr68Yb", DockerContainerID: "ZKXr68Yb",
@ -4563,6 +4601,7 @@ func TestFullConfig(t *testing.T) {
"1UJXjVrT": {"OJgxzTfk", "xZZrFsq7"}, "1UJXjVrT": {"OJgxzTfk", "xZZrFsq7"},
}, },
Method: "5wkAxCUE", Method: "5wkAxCUE",
OutputMaxSize: checks.DefaultBufSize,
TCP: "MN3oA9D2", TCP: "MN3oA9D2",
Interval: 32718 * time.Second, Interval: 32718 * time.Second,
DockerContainerID: "cU15LMet", DockerContainerID: "cU15LMet",
@ -4584,6 +4623,7 @@ func TestFullConfig(t *testing.T) {
"vr7wY7CS": {"EtCoNPPL", "9vAarJ5s"}, "vr7wY7CS": {"EtCoNPPL", "9vAarJ5s"},
}, },
Method: "wzByP903", Method: "wzByP903",
OutputMaxSize: checks.DefaultBufSize,
TCP: "2exjZIGE", TCP: "2exjZIGE",
Interval: 5656 * time.Second, Interval: 5656 * time.Second,
DockerContainerID: "5tDBWpfA", DockerContainerID: "5tDBWpfA",
@ -4679,6 +4719,7 @@ func TestFullConfig(t *testing.T) {
"SHOVq1Vv": {"jntFhyym", "GYJh32pp"}, "SHOVq1Vv": {"jntFhyym", "GYJh32pp"},
}, },
Method: "T66MFBfR", Method: "T66MFBfR",
OutputMaxSize: checks.DefaultBufSize,
TCP: "bNnNfx2A", TCP: "bNnNfx2A",
Interval: 22224 * time.Second, Interval: 22224 * time.Second,
DockerContainerID: "ipgdFtjd", DockerContainerID: "ipgdFtjd",
@ -4700,6 +4741,7 @@ func TestFullConfig(t *testing.T) {
"p2UI34Qz": {"UsG1D0Qh", "NHhRiB6s"}, "p2UI34Qz": {"UsG1D0Qh", "NHhRiB6s"},
}, },
Method: "ciYHWors", Method: "ciYHWors",
OutputMaxSize: checks.DefaultBufSize,
TCP: "FfvCwlqH", TCP: "FfvCwlqH",
Interval: 12356 * time.Second, Interval: 12356 * time.Second,
DockerContainerID: "HBndBU6R", DockerContainerID: "HBndBU6R",
@ -4721,6 +4763,7 @@ func TestFullConfig(t *testing.T) {
"l4HwQ112": {"fk56MNlo", "dhLK56aZ"}, "l4HwQ112": {"fk56MNlo", "dhLK56aZ"},
}, },
Method: "9afLm3Mj", Method: "9afLm3Mj",
OutputMaxSize: checks.DefaultBufSize,
TCP: "fjiLFqVd", TCP: "fjiLFqVd",
Interval: 23926 * time.Second, Interval: 23926 * time.Second,
DockerContainerID: "dO5TtRHk", DockerContainerID: "dO5TtRHk",
@ -4777,6 +4820,7 @@ func TestFullConfig(t *testing.T) {
"wan": "78.63.37.19", "wan": "78.63.37.19",
}, },
TranslateWANAddrs: true, TranslateWANAddrs: true,
UIContentPath: "/ui/",
UIDir: "11IFzAUn", UIDir: "11IFzAUn",
UnixSocketUser: "E0nB1DwA", UnixSocketUser: "E0nB1DwA",
UnixSocketGroup: "8pFodrV8", UnixSocketGroup: "8pFodrV8",
@ -5058,6 +5102,7 @@ func TestConfigDecodeBytes(t *testing.T) {
func TestSanitize(t *testing.T) { func TestSanitize(t *testing.T) {
rt := RuntimeConfig{ rt := RuntimeConfig{
BindAddr: &net.IPAddr{IP: net.ParseIP("127.0.0.1")}, BindAddr: &net.IPAddr{IP: net.ParseIP("127.0.0.1")},
CheckOutputMaxSize: checks.DefaultBufSize,
SerfAdvertiseAddrLAN: &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678}, SerfAdvertiseAddrLAN: &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678},
DNSAddrs: []net.Addr{ DNSAddrs: []net.Addr{
&net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678}, &net.TCPAddr{IP: net.ParseIP("1.2.3.4"), Port: 5678},
@ -5080,7 +5125,8 @@ func TestSanitize(t *testing.T) {
Name: "foo", Name: "foo",
Token: "bar", Token: "bar",
Check: structs.CheckType{ Check: structs.CheckType{
Name: "blurb", Name: "blurb",
OutputMaxSize: checks.DefaultBufSize,
}, },
Weights: &structs.Weights{ Weights: &structs.Weights{
Passing: 67, Passing: 67,
@ -5090,8 +5136,9 @@ func TestSanitize(t *testing.T) {
}, },
Checks: []*structs.CheckDefinition{ Checks: []*structs.CheckDefinition{
&structs.CheckDefinition{ &structs.CheckDefinition{
Name: "zoo", Name: "zoo",
Token: "zope", Token: "zope",
OutputMaxSize: checks.DefaultBufSize,
}, },
}, },
} }
@ -5131,6 +5178,7 @@ func TestSanitize(t *testing.T) {
"CAPath": "", "CAPath": "",
"CertFile": "", "CertFile": "",
"CheckDeregisterIntervalMin": "0s", "CheckDeregisterIntervalMin": "0s",
"CheckOutputMaxSize": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"CheckReapInterval": "0s", "CheckReapInterval": "0s",
"CheckUpdateInterval": "0s", "CheckUpdateInterval": "0s",
"Checks": [{ "Checks": [{
@ -5147,6 +5195,7 @@ func TestSanitize(t *testing.T) {
"Method": "", "Method": "",
"Name": "zoo", "Name": "zoo",
"Notes": "", "Notes": "",
"OutputMaxSize": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"ScriptArgs": [], "ScriptArgs": [],
"ServiceID": "", "ServiceID": "",
"Shell": "", "Shell": "",
@ -5178,6 +5227,7 @@ func TestSanitize(t *testing.T) {
"ConsulCoordinateUpdateMaxBatches": 0, "ConsulCoordinateUpdateMaxBatches": 0,
"ConsulCoordinateUpdatePeriod": "15s", "ConsulCoordinateUpdatePeriod": "15s",
"ConsulRaftElectionTimeout": "0s", "ConsulRaftElectionTimeout": "0s",
"CheckOutputMaxSize": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"ConsulRaftHeartbeatTimeout": "0s", "ConsulRaftHeartbeatTimeout": "0s",
"ConsulRaftLeaderLeaseTimeout": "0s", "ConsulRaftLeaderLeaseTimeout": "0s",
"GossipLANGossipInterval": "0s", "GossipLANGossipInterval": "0s",
@ -5317,6 +5367,7 @@ func TestSanitize(t *testing.T) {
"Method": "", "Method": "",
"Name": "blurb", "Name": "blurb",
"Notes": "", "Notes": "",
"OutputMaxSize": ` + strconv.Itoa(checks.DefaultBufSize) + `,
"ScriptArgs": [], "ScriptArgs": [],
"Shell": "", "Shell": "",
"Status": "", "Status": "",
@ -5380,6 +5431,7 @@ func TestSanitize(t *testing.T) {
"StatsiteAddr": "" "StatsiteAddr": ""
}, },
"TranslateWANAddrs": false, "TranslateWANAddrs": false,
"UIContentPath": "",
"UIDir": "", "UIDir": "",
"UnixSocketGroup": "", "UnixSocketGroup": "",
"UnixSocketMode": "", "UnixSocketMode": "",
@ -5720,6 +5772,41 @@ func TestReadPath(t *testing.T) {
} }
} }
func Test_UIPathBuilder(t *testing.T) {
cases := []struct {
name string
path string
expected string
}{
{
"Letters only string",
"hello",
"/hello/",
},
{
"Alphanumeric",
"Hello1",
"/Hello1/",
},
{
"Hyphen and underscore",
"-_",
"/-_/",
},
{
"Many slashes",
"/hello/ui/1/",
"/hello/ui/1/",
},
}
for _, tt := range cases {
actual := UIPathBuilder(tt.path)
require.Equal(t, tt.expected, actual)
}
}
func splitIPPort(hostport string) (net.IP, int) { func splitIPPort(hostport string) (net.IP, int) {
h, p, err := net.SplitHostPort(hostport) h, p, err := net.SplitHostPort(hostport)
if err != nil { if err != nil {

View File

@ -7,6 +7,7 @@ import (
"os" "os"
"time" "time"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/consul/autopilot" "github.com/hashicorp/consul/agent/consul/autopilot"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
@ -368,6 +369,9 @@ type Config struct {
// warning and discard the remaining updates. // warning and discard the remaining updates.
CoordinateUpdateMaxBatches int CoordinateUpdateMaxBatches int
// CheckOutputMaxSize control the max size of output of checks
CheckOutputMaxSize int
// RPCHoldTimeout is how long an RPC can be "held" before it is errored. // RPCHoldTimeout is how long an RPC can be "held" before it is errored.
// This is used to paper over a loss of leadership by instead holding RPCs, // This is used to paper over a loss of leadership by instead holding RPCs,
// so that the caller experiences a slow response rather than an error. // so that the caller experiences a slow response rather than an error.
@ -502,6 +506,8 @@ func DefaultConfig() *Config {
CoordinateUpdateBatchSize: 128, CoordinateUpdateBatchSize: 128,
CoordinateUpdateMaxBatches: 5, CoordinateUpdateMaxBatches: 5,
CheckOutputMaxSize: checks.DefaultBufSize,
RPCRate: rate.Inf, RPCRate: rate.Inf,
RPCMaxBurst: 1000, RPCMaxBurst: 1000,

View File

@ -1363,6 +1363,17 @@ func (s *Server) handleAliveMember(member serf.Member) error {
ID: structs.ConsulServiceID, ID: structs.ConsulServiceID,
Service: structs.ConsulServiceName, Service: structs.ConsulServiceName,
Port: parts.Port, Port: parts.Port,
Weights: &structs.Weights{
Passing: 1,
Warning: 1,
},
Meta: map[string]string{
"raft_version": strconv.Itoa(parts.RaftVersion),
"serf_protocol_current": strconv.FormatUint(uint64(member.ProtocolCur), 10),
"serf_protocol_min": strconv.FormatUint(uint64(member.ProtocolMin), 10),
"serf_protocol_max": strconv.FormatUint(uint64(member.ProtocolMax), 10),
"version": parts.Build.String(),
},
} }
// Attempt to join the consul server // Attempt to join the consul server

View File

@ -1,17 +1,21 @@
package agent package agent
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
"net/url" "net/url"
"os"
"reflect" "reflect"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"text/template"
"time" "time"
"github.com/NYTimes/gziphandler" "github.com/NYTimes/gziphandler"
@ -83,6 +87,66 @@ type HTTPServer struct {
// proto is filled by the agent to "http" or "https". // proto is filled by the agent to "http" or "https".
proto string proto string
} }
type templatedFile struct {
templated *bytes.Reader
name string
mode os.FileMode
modTime time.Time
}
func newTemplatedFile(buf *bytes.Buffer, raw http.File) *templatedFile {
info, _ := raw.Stat()
return &templatedFile{
templated: bytes.NewReader(buf.Bytes()),
name: info.Name(),
mode: info.Mode(),
modTime: info.ModTime(),
}
}
func (t *templatedFile) Read(p []byte) (n int, err error) {
return t.templated.Read(p)
}
func (t *templatedFile) Seek(offset int64, whence int) (int64, error) {
return t.templated.Seek(offset, whence)
}
func (t *templatedFile) Close() error {
return nil
}
func (t *templatedFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, errors.New("not a directory")
}
func (t *templatedFile) Stat() (os.FileInfo, error) {
return t, nil
}
func (t *templatedFile) Name() string {
return t.name
}
func (t *templatedFile) Size() int64 {
return int64(t.templated.Len())
}
func (t *templatedFile) Mode() os.FileMode {
return t.mode
}
func (t *templatedFile) ModTime() time.Time {
return t.modTime
}
func (t *templatedFile) IsDir() bool {
return false
}
func (t *templatedFile) Sys() interface{} {
return nil
}
type redirectFS struct { type redirectFS struct {
fs http.FileSystem fs http.FileSystem
@ -96,6 +160,25 @@ func (fs *redirectFS) Open(name string) (http.File, error) {
return file, err return file, err
} }
type templatedIndexFS struct {
fs http.FileSystem
ContentPath string
}
func (fs *templatedIndexFS) Open(name string) (http.File, error) {
file, err := fs.fs.Open(name)
if err == nil && name == "/index.html" {
content, _ := ioutil.ReadAll(file)
file.Seek(0, 0)
t, _ := template.New("fmtedindex").Parse(string(content))
var out bytes.Buffer
err = t.Execute(&out, fs)
file = newTemplatedFile(&out, file)
}
return file, err
}
// endpoint is a Consul-specific HTTP handler that takes the usual arguments in // endpoint is a Consul-specific HTTP handler that takes the usual arguments in
// but returns a response object and error, both of which are handled in a // but returns a response object and error, both of which are handled in a
// common manner by Consul's HTTP server. // common manner by Consul's HTTP server.
@ -207,7 +290,6 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
handleFuncMetrics(pattern, http.HandlerFunc(wrapper)) handleFuncMetrics(pattern, http.HandlerFunc(wrapper))
} }
mux.HandleFunc("/", s.Index) mux.HandleFunc("/", s.Index)
for pattern, fn := range endpoints { for pattern, fn := range endpoints {
thisFn := fn thisFn := fn
@ -227,7 +309,6 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
if s.IsUIEnabled() { if s.IsUIEnabled() {
var uifs http.FileSystem var uifs http.FileSystem
// Use the custom UI dir if provided. // Use the custom UI dir if provided.
if s.agent.config.UIDir != "" { if s.agent.config.UIDir != "" {
uifs = http.Dir(s.agent.config.UIDir) uifs = http.Dir(s.agent.config.UIDir)
@ -235,10 +316,9 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
fs := assetFS() fs := assetFS()
uifs = fs uifs = fs
} }
uifs = &redirectFS{fs: uifs} uifs = &redirectFS{fs: &templatedIndexFS{fs: uifs, ContentPath: s.agent.config.UIContentPath}}
mux.Handle("/robots.txt", http.FileServer(uifs)) mux.Handle("/robots.txt", http.FileServer(uifs))
mux.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(uifs))) mux.Handle(s.agent.config.UIContentPath, http.StripPrefix(s.agent.config.UIContentPath, http.FileServer(uifs)))
} }
// Wrap the whole mux with a handler that bans URLs with non-printable // Wrap the whole mux with a handler that bans URLs with non-printable
@ -489,7 +569,7 @@ func (s *HTTPServer) Index(resp http.ResponseWriter, req *http.Request) {
} }
// Redirect to the UI endpoint // Redirect to the UI endpoint
http.Redirect(resp, req, "/ui/", http.StatusMovedPermanently) // 301 http.Redirect(resp, req, s.agent.config.UIContentPath, http.StatusMovedPermanently) // 301
} }
// decodeBody is used to decode a JSON request body // decodeBody is used to decode a JSON request body

View File

@ -37,6 +37,7 @@ type CheckDefinition struct {
Timeout time.Duration Timeout time.Duration
TTL time.Duration TTL time.Duration
DeregisterCriticalServiceAfter time.Duration DeregisterCriticalServiceAfter time.Duration
OutputMaxSize int
} }
func (c *CheckDefinition) HealthCheck(node string) *HealthCheck { func (c *CheckDefinition) HealthCheck(node string) *HealthCheck {
@ -72,6 +73,7 @@ func (c *CheckDefinition) CheckType() *CheckType {
GRPCUseTLS: c.GRPCUseTLS, GRPCUseTLS: c.GRPCUseTLS,
Header: c.Header, Header: c.Header,
Method: c.Method, Method: c.Method,
OutputMaxSize: c.OutputMaxSize,
TCP: c.TCP, TCP: c.TCP,
Interval: c.Interval, Interval: c.Interval,
DockerContainerID: c.DockerContainerID, DockerContainerID: c.DockerContainerID,

View File

@ -45,6 +45,7 @@ type CheckType struct {
// service, if any, to be deregistered if this check is critical for // service, if any, to be deregistered if this check is critical for
// longer than this duration. // longer than this duration.
DeregisterCriticalServiceAfter time.Duration DeregisterCriticalServiceAfter time.Duration
OutputMaxSize int
} }
type CheckTypes []*CheckType type CheckTypes []*CheckType
@ -67,6 +68,9 @@ func (c *CheckType) Validate() error {
if !intervalCheck && !c.IsAlias() && c.TTL <= 0 { if !intervalCheck && !c.IsAlias() && c.TTL <= 0 {
return fmt.Errorf("TTL must be > 0 for TTL checks") return fmt.Errorf("TTL must be > 0 for TTL checks")
} }
if c.OutputMaxSize < 0 {
return fmt.Errorf("MaxOutputMaxSize must be positive")
}
return nil return nil
} }

View File

@ -999,6 +999,7 @@ type HealthCheckDefinition struct {
Method string `json:",omitempty"` Method string `json:",omitempty"`
TCP string `json:",omitempty"` TCP string `json:",omitempty"`
Interval time.Duration `json:",omitempty"` Interval time.Duration `json:",omitempty"`
OutputMaxSize uint `json:",omitempty"`
Timeout time.Duration `json:",omitempty"` Timeout time.Duration `json:",omitempty"`
DeregisterCriticalServiceAfter time.Duration `json:",omitempty"` DeregisterCriticalServiceAfter time.Duration `json:",omitempty"`
} }
@ -1007,11 +1008,13 @@ func (d *HealthCheckDefinition) MarshalJSON() ([]byte, error) {
type Alias HealthCheckDefinition type Alias HealthCheckDefinition
exported := &struct { exported := &struct {
Interval string `json:",omitempty"` Interval string `json:",omitempty"`
OutputMaxSize uint `json:",omitempty"`
Timeout string `json:",omitempty"` Timeout string `json:",omitempty"`
DeregisterCriticalServiceAfter string `json:",omitempty"` DeregisterCriticalServiceAfter string `json:",omitempty"`
*Alias *Alias
}{ }{
Interval: d.Interval.String(), Interval: d.Interval.String(),
OutputMaxSize: d.OutputMaxSize,
Timeout: d.Timeout.String(), Timeout: d.Timeout.String(),
DeregisterCriticalServiceAfter: d.DeregisterCriticalServiceAfter.String(), DeregisterCriticalServiceAfter: d.DeregisterCriticalServiceAfter.String(),
Alias: (*Alias)(d), Alias: (*Alias)(d),

View File

@ -974,7 +974,7 @@ function ui_version {
return 1 return 1
fi fi
local ui_version="$(grep '<!-- CONSUL_VERSION: .* -->$' "$1" | sed 's/^<!-- CONSUL_VERSION: \(.*\) -->$/\1/')" || return 1 local ui_version="$(grep '<!-- CONSUL_VERSION: .* -->' "$1" | sed 's/<!-- CONSUL_VERSION: \(.*\) -->/\1/' | xargs)" || return 1
echo "$ui_version" echo "$ui_version"
return 0 return 0
} }

View File

@ -2,7 +2,13 @@ ROOT:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
all: build all: build
deps: node_modules deps: node_modules clean
clean:
rm -rf ./tmp
build-ci: deps
yarn run build-ci --output-path=dist
build: deps build: deps
yarn run build yarn run build
@ -19,6 +25,9 @@ test: deps
test-view: deps test-view: deps
yarn run test:view yarn run test:view
test-parallel: deps
yarn test-parallel
lint: deps lint: deps
yarn run lint:js yarn run lint:js
@ -31,4 +40,4 @@ steps:
node_modules: yarn.lock package.json node_modules: yarn.lock package.json
yarn install yarn install
.PHONY: all deps build start test test-view lint format .PHONY: all deps build start test test-view lint format clean

View File

@ -6,51 +6,12 @@
<title>Consul by HashiCorp</title> <title>Consul by HashiCorp</title>
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "// all HTML content is populated via ./lib/startup/index"}}
{{content-for "head"}} {{content-for "head"}}
<link rel="icon" type="image/png" href="{{rootURL}}assets/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="{{rootURL}}assets/favicon-16x16.png" sizes="16x16">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/consul-ui.css">
{{content-for "head-footer"}} {{content-for "head-footer"}}
</head> </head>
<body> <body>
<noscript>
<div style="margin: 0 auto;">
<h2>JavaScript Required</h2>
<p>Please enable JavaScript in your web browser to use Consul UI.</p>
</div>
</noscript>
{{content-for "body"}} {{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script>
var appendScript = function(src) {
var $script = document.createElement('script');
$script.src = src;
document.body.appendChild($script);
}
if(!('TextDecoder' in window)) {
appendScript('{{rootURL}}assets/encoding-indexes.js');
appendScript('{{rootURL}}assets/encoding.js');
}
</script>
<script src="{{rootURL}}assets/consul-ui.js"></script>
<script>
CodeMirror.modeURL = {
replace: function(n, mode) {
switch(mode) {
case 'javascript':
return '{{rootURL}}assets/codemirror/mode/javascript/javascript.js';
case 'ruby':
return '{{rootURL}}assets/codemirror/mode/ruby/ruby.js';
case 'yaml':
return '{{rootURL}}assets/codemirror/mode/yaml/yaml.js';
}
}
};
</script>
{{content-for "body-footer"}} {{content-for "body-footer"}}
</body> </body>
</html> </html>

View File

@ -3,16 +3,17 @@
module.exports = { module.exports = {
name: 'startup', name: 'startup',
contentFor: function(type, config) { contentFor: function(type, config) {
const enterprise = config.CONSUL_BINARY_TYPE !== 'oss' && config.CONSUL_BINARY_TYPE !== ''; const vars = {
appName: config.modulePrefix,
environment: config.environment,
rootURL: config.environment === 'production' ? '{{.ContentPath}}' : config.rootURL,
config: config,
};
switch (type) { switch (type) {
case 'head': case 'head':
return `<!-- CONSUL_VERSION: ${config.CONSUL_VERSION} -->`; return require('./templates/head.html.js')(vars);
case 'body': case 'body':
return `<svg width="168" height="53" xmlns="http://www.w3.org/2000/svg"><g fill="#919FA8" fill-rule="evenodd"><path d="M26.078 32.12a5.586 5.586 0 1 1 5.577-5.599 5.577 5.577 0 0 1-5.577 5.6M37.009 29.328a2.56 2.56 0 1 1 2.56-2.56 2.551 2.551 0 0 1-2.56 2.56M46.916 31.669a2.56 2.56 0 1 1 .051-.21c-.028.066-.028.13-.051.21M44.588 25.068a2.565 2.565 0 0 1-2.672-.992 2.558 2.558 0 0 1-.102-2.845 2.564 2.564 0 0 1 4.676.764c.072.328.081.667.027 1a2.463 2.463 0 0 1-1.925 2.073M53.932 31.402a2.547 2.547 0 0 1-2.95 2.076 2.559 2.559 0 0 1-2.064-2.965 2.547 2.547 0 0 1 2.948-2.077 2.57 2.57 0 0 1 2.128 2.716.664.664 0 0 0-.05.228M51.857 25.103a2.56 2.56 0 1 1 2.108-2.945c.034.218.043.439.027.658a2.547 2.547 0 0 1-2.135 2.287M49.954 40.113a2.56 2.56 0 1 1 .314-1.037c-.02.366-.128.721-.314 1.037M48.974 16.893a2.56 2.56 0 1 1 .97-3.487c.264.446.375.965.317 1.479a2.56 2.56 0 0 1-1.287 2.008"/><path d="M26.526 52.603c-14.393 0-26.06-11.567-26.06-25.836C.466 12.498 12.133.931 26.526.931a25.936 25.936 0 0 1 15.836 5.307l-3.167 4.117A20.962 20.962 0 0 0 17.304 8.23C10.194 11.713 5.7 18.9 5.714 26.763c-.014 7.862 4.48 15.05 11.59 18.534a20.962 20.962 0 0 0 21.89-2.127l3.168 4.123a25.981 25.981 0 0 1-15.836 5.31z"/>${ return require('./templates/body.html.js')(vars);
enterprise
? `<path data-enterprise-logo d="M61 42.083h3.975v.785H61.87v2.136h2.882v.784H61.87v2.31h3.114v.785H61v-6.8zm6.907 1.018V48.9h-.828v-6.817h1.2l2.94 5.84v-5.84h.829V48.9h-1.193L67.907 43.1zm7.826-.225h-2.012v-.784h4.911v.784h-2.02V48.9h-.879v-6.024zm4.564-.793h3.975v.785h-3.106v2.136h2.882v.784h-2.882v2.31h3.114v.785h-3.992l.009-6.8zm8.605 4.347h-1.657v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.657v2.778h1.657c.828 0 1.118-.234 1.118-.901v-.968c.024-.676-.265-.901-1.094-.901l-.024-.008zm4.488-.785h2.485c1.45 0 1.963.635 1.963 1.67v1.009c0 1.05-.505 1.668-1.963 1.668H94.3v2.47h-.87l-.04-6.817zm2.419.785h-1.54v2.803h1.54c.828 0 1.118-.234 1.118-.901v-1.001c0-.668-.282-.893-1.118-.893v-.008zm6.368 3.562h-1.656v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.656v2.778h1.656c.829 0 1.118-.234 1.118-.901v-.968c.017-.676-.265-.901-1.101-.901l-.017-.008zm5.392 6.032h-.828v-6.817h.828V48.9zm4.14.1a5.76 5.76 0 0 1-2.012-.359l.141-.717c.605.195 1.236.3 1.872.308 1.085 0 1.308-.283 1.308-1.06 0-.917 0-1-1.4-1.317-1.656-.368-1.83-.685-1.83-2.095 0-1.184.49-1.76 2.162-1.76a7.648 7.648 0 0 1 1.83.225l-.074.743a8.223 8.223 0 0 0-1.74-.192c-1.11 0-1.308.225-1.308 1.01 0 .942 0 .984 1.342 1.318 1.797.45 1.888.717 1.888 2.044.033 1.176-.315 1.852-2.178 1.852zm4.332-6.917h3.95v.785h-3.105v2.136h2.882v.784h-2.882v2.31H120v.785h-3.992l.033-6.8z" fill-rule="nonzero"/>`
: ''
}<path d="M61 30.15V17.948c0-4.962 2.845-7.85 9.495-7.85 2.484 0 5.048.326 7.252.895l-.561 4.433c-2.164-.406-4.688-.691-6.53-.691-3.486 0-4.608 1.22-4.608 4.108v10.412c0 2.888 1.122 4.108 4.607 4.108 1.843 0 4.367-.284 6.53-.691l.562 4.433c-2.204.57-4.768.895-7.252.895C63.845 38 61 35.112 61 30.15zm36.808.04c0 4.068-1.802 7.81-8.493 7.81-6.69 0-8.494-3.742-8.494-7.81v-5.002c0-4.067 1.803-7.81 8.494-7.81 6.69 0 8.493 3.743 8.493 7.81v5.003zm-4.887-5.165c0-2.237-1.002-3.416-3.606-3.416s-3.606 1.18-3.606 3.416v5.328c0 2.237 1.002 3.417 3.606 3.417s3.606-1.18 3.606-3.417v-5.328zm25.79 12.568h-4.887V23.764c0-1.057-.44-1.586-1.563-1.586-1.201 0-3.325.732-5.088 1.668v13.747h-4.887V17.785h3.726l.48 1.668c2.444-1.22 5.53-2.074 7.813-2.074 3.245 0 4.407 2.318 4.407 5.857v14.357zm18.26-5.775c0 3.823-1.162 6.182-7.052 6.182-2.083 0-4.927-.488-6.73-1.139l.68-3.782c1.643.488 3.807.854 5.81.854 2.164 0 2.484-.488 2.484-1.993 0-1.22-.24-1.83-3.405-2.603-4.768-1.18-5.329-2.4-5.329-6.223 0-3.986 1.723-5.735 7.292-5.735 1.803 0 4.166.244 5.85.691l-.482 3.945c-1.482-.284-3.846-.569-5.368-.569-2.124 0-2.484.488-2.484 1.708 0 1.587.12 1.709 2.764 2.4 5.449 1.464 5.97 2.196 5.97 6.264zm4.357-14.033h4.887v13.83c0 1.057.441 1.586 1.563 1.586 1.202 0 3.325-.733 5.088-1.668V17.785h4.888v19.808h-3.726l-.481-1.667c-2.444 1.22-5.529 2.074-7.812 2.074-3.246 0-4.407-2.318-4.407-5.857V17.785zM168 37.593h-4.888V9.691L168 9v28.593z"/></g></svg>`;
case 'root-class': case 'root-class':
return 'ember-loading'; return 'ember-loading';
} }

View File

@ -0,0 +1,42 @@
module.exports = ({ appName, environment, rootURL, config }) => `
<noscript>
<div style="margin: 0 auto;">
<h2>JavaScript Required</h2>
<p>Please enable JavaScript in your web browser to use Consul UI.</p>
</div>
</noscript>
<svg width="168" height="53" xmlns="http://www.w3.org/2000/svg"><g fill="#919FA8" fill-rule="evenodd"><path d="M26.078 32.12a5.586 5.586 0 1 1 5.577-5.599 5.577 5.577 0 0 1-5.577 5.6M37.009 29.328a2.56 2.56 0 1 1 2.56-2.56 2.551 2.551 0 0 1-2.56 2.56M46.916 31.669a2.56 2.56 0 1 1 .051-.21c-.028.066-.028.13-.051.21M44.588 25.068a2.565 2.565 0 0 1-2.672-.992 2.558 2.558 0 0 1-.102-2.845 2.564 2.564 0 0 1 4.676.764c.072.328.081.667.027 1a2.463 2.463 0 0 1-1.925 2.073M53.932 31.402a2.547 2.547 0 0 1-2.95 2.076 2.559 2.559 0 0 1-2.064-2.965 2.547 2.547 0 0 1 2.948-2.077 2.57 2.57 0 0 1 2.128 2.716.664.664 0 0 0-.05.228M51.857 25.103a2.56 2.56 0 1 1 2.108-2.945c.034.218.043.439.027.658a2.547 2.547 0 0 1-2.135 2.287M49.954 40.113a2.56 2.56 0 1 1 .314-1.037c-.02.366-.128.721-.314 1.037M48.974 16.893a2.56 2.56 0 1 1 .97-3.487c.264.446.375.965.317 1.479a2.56 2.56 0 0 1-1.287 2.008"/><path d="M26.526 52.603c-14.393 0-26.06-11.567-26.06-25.836C.466 12.498 12.133.931 26.526.931a25.936 25.936 0 0 1 15.836 5.307l-3.167 4.117A20.962 20.962 0 0 0 17.304 8.23C10.194 11.713 5.7 18.9 5.714 26.763c-.014 7.862 4.48 15.05 11.59 18.534a20.962 20.962 0 0 0 21.89-2.127l3.168 4.123a25.981 25.981 0 0 1-15.836 5.31z"/>${
config.CONSUL_BINARY_TYPE !== 'oss' && config.CONSUL_BINARY_TYPE !== ''
? `<path data-enterprise-logo d="M61 42.083h3.975v.785H61.87v2.136h2.882v.784H61.87v2.31h3.114v.785H61v-6.8zm6.907 1.018V48.9h-.828v-6.817h1.2l2.94 5.84v-5.84h.829V48.9h-1.193L67.907 43.1zm7.826-.225h-2.012v-.784h4.911v.784h-2.02V48.9h-.879v-6.024zm4.564-.793h3.975v.785h-3.106v2.136h2.882v.784h-2.882v2.31h3.114v.785h-3.992l.009-6.8zm8.605 4.347h-1.657v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.657v2.778h1.657c.828 0 1.118-.234 1.118-.901v-.968c.024-.676-.265-.901-1.094-.901l-.024-.008zm4.488-.785h2.485c1.45 0 1.963.635 1.963 1.67v1.009c0 1.05-.505 1.668-1.963 1.668H94.3v2.47h-.87l-.04-6.817zm2.419.785h-1.54v2.803h1.54c.828 0 1.118-.234 1.118-.901v-1.001c0-.668-.282-.893-1.118-.893v-.008zm6.368 3.562h-1.656v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.656v2.778h1.656c.829 0 1.118-.234 1.118-.901v-.968c.017-.676-.265-.901-1.101-.901l-.017-.008zm5.392 6.032h-.828v-6.817h.828V48.9zm4.14.1a5.76 5.76 0 0 1-2.012-.359l.141-.717c.605.195 1.236.3 1.872.308 1.085 0 1.308-.283 1.308-1.06 0-.917 0-1-1.4-1.317-1.656-.368-1.83-.685-1.83-2.095 0-1.184.49-1.76 2.162-1.76a7.648 7.648 0 0 1 1.83.225l-.074.743a8.223 8.223 0 0 0-1.74-.192c-1.11 0-1.308.225-1.308 1.01 0 .942 0 .984 1.342 1.318 1.797.45 1.888.717 1.888 2.044.033 1.176-.315 1.852-2.178 1.852zm4.332-6.917h3.95v.785h-3.105v2.136h2.882v.784h-2.882v2.31H120v.785h-3.992l.033-6.8z" fill-rule="nonzero"/>`
: ``
}<path d="M61 30.15V17.948c0-4.962 2.845-7.85 9.495-7.85 2.484 0 5.048.326 7.252.895l-.561 4.433c-2.164-.406-4.688-.691-6.53-.691-3.486 0-4.608 1.22-4.608 4.108v10.412c0 2.888 1.122 4.108 4.607 4.108 1.843 0 4.367-.284 6.53-.691l.562 4.433c-2.204.57-4.768.895-7.252.895C63.845 38 61 35.112 61 30.15zm36.808.04c0 4.068-1.802 7.81-8.493 7.81-6.69 0-8.494-3.742-8.494-7.81v-5.002c0-4.067 1.803-7.81 8.494-7.81 6.69 0 8.493 3.743 8.493 7.81v5.003zm-4.887-5.165c0-2.237-1.002-3.416-3.606-3.416s-3.606 1.18-3.606 3.416v5.328c0 2.237 1.002 3.417 3.606 3.417s3.606-1.18 3.606-3.417v-5.328zm25.79 12.568h-4.887V23.764c0-1.057-.44-1.586-1.563-1.586-1.201 0-3.325.732-5.088 1.668v13.747h-4.887V17.785h3.726l.48 1.668c2.444-1.22 5.53-2.074 7.813-2.074 3.245 0 4.407 2.318 4.407 5.857v14.357zm18.26-5.775c0 3.823-1.162 6.182-7.052 6.182-2.083 0-4.927-.488-6.73-1.139l.68-3.782c1.643.488 3.807.854 5.81.854 2.164 0 2.484-.488 2.484-1.993 0-1.22-.24-1.83-3.405-2.603-4.768-1.18-5.329-2.4-5.329-6.223 0-3.986 1.723-5.735 7.292-5.735 1.803 0 4.166.244 5.85.691l-.482 3.945c-1.482-.284-3.846-.569-5.368-.569-2.124 0-2.484.488-2.484 1.708 0 1.587.12 1.709 2.764 2.4 5.449 1.464 5.97 2.196 5.97 6.264zm4.357-14.033h4.887v13.83c0 1.057.441 1.586 1.563 1.586 1.202 0 3.325-.733 5.088-1.668V17.785h4.888v19.808h-3.726l-.481-1.667c-2.444 1.22-5.529 2.074-7.812 2.074-3.246 0-4.407-2.318-4.407-5.857V17.785zM168 37.593h-4.888V9.691L168 9v28.593z"/></g></svg>
<script src="${rootURL}assets/vendor.js"></script>
${environment === 'test' ? `<script src="${rootURL}assets/test-support.js"></script>` : ``}
<script>
var appendScript = function(src) {
var $script = document.createElement('script');
$script.src = src;
document.body.appendChild($script);
}
if(!('TextDecoder' in window)) {
appendScript('${rootURL}assets/encoding-indexes.js');
appendScript('${rootURL}assets/encoding.js');
}
</script>
<script src="${rootURL}assets/${appName}.js"></script>
<script>
CodeMirror.modeURL = {
replace: function(n, mode) {
switch(mode) {
case 'javascript':
return '${rootURL}assets/codemirror/mode/javascript/javascript.js';
case 'ruby':
return '${rootURL}assets/codemirror/mode/ruby/ruby.js';
case 'yaml':
return '${rootURL}assets/codemirror/mode/yaml/yaml.js';
}
}
};
</script>
${environment === 'test' ? `<script src="${rootURL}assets/tests.js"></script>` : ``}
`;

View File

@ -0,0 +1,37 @@
module.exports = ({ appName, environment, rootURL, config }) => `
<!-- CONSUL_VERSION: ${config.CONSUL_VERSION} -->
<script>
var setConfig = function(appName, config) {
var $meta = document.querySelector('meta[name="' + appName + '/config/environment"]');
var defaultConfig = JSON.parse(decodeURIComponent($meta.getAttribute('content')));
(
function set(blob, config) {
Object.keys(config).forEach(
function(key) {
var value = config[key];
if(Object.prototype.toString.call(value) === '[object Object]') {
set(blob[key], config[key]);
} else {
blob[key] = config[key];
}
}
);
}
)(defaultConfig, config);
$meta.setAttribute('content', encodeURIComponent(JSON.stringify(defaultConfig)));
}
setConfig(
'${appName}',
{
rootURL: '${rootURL}'
}
);
</script>
<link rel="icon" type="image/png" href="${rootURL}assets/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="${rootURL}assets/favicon-16x16.png" sizes="16x16">
<link integrity="" rel="stylesheet" href="${rootURL}assets/vendor.css">
<link integrity="" rel="stylesheet" href="${rootURL}assets/${appName}.css">
${
environment === 'test' ? `<link rel="stylesheet" href="${rootURL}assets/test-support.css">` : ``
}
`;

View File

@ -6,14 +6,9 @@
<title>ConsulUi Tests</title> <title>ConsulUi Tests</title>
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}} {{content-for "head"}}
{{content-for "test-head"}} {{content-for "test-head"}}
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/consul-ui.css">
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
{{content-for "head-footer"}} {{content-for "head-footer"}}
{{content-for "test-head-footer"}} {{content-for "test-head-footer"}}
</head> </head>
@ -22,10 +17,6 @@
{{content-for "test-body"}} {{content-for "test-body"}}
<script src="/testem.js" integrity=""></script> <script src="/testem.js" integrity=""></script>
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/test-support.js"></script>
<script src="{{rootURL}}assets/consul-ui.js"></script>
<script src="{{rootURL}}assets/tests.js"></script>
{{content-for "body-footer"}} {{content-for "body-footer"}}
{{content-for "test-body-footer"}} {{content-for "test-body-footer"}}

View File

@ -183,6 +183,12 @@ The table below shows this endpoint's support for
case of a Script, HTTP, TCP, or gRPC check. Can be specified in the form of "10s" case of a Script, HTTP, TCP, or gRPC check. Can be specified in the form of "10s"
or "5m" (i.e., 10 seconds or 5 minutes, respectively). or "5m" (i.e., 10 seconds or 5 minutes, respectively).
- `OutputMaxSize` `(positive int: 4096)` - Allow to put a maximum size of text
for the given check. This value must be greater than 0, by default, the value
is 4k.
The value can be further limited for all checks of a given agent using the
`check_output_max_size` flag in the agent.
- `TLSSkipVerify` `(bool: false)` - Specifies if the certificate for an HTTPS - `TLSSkipVerify` `(bool: false)` - Specifies if the certificate for an HTTPS
check should not be verified. check should not be verified.

View File

@ -132,6 +132,14 @@ will exit with an error at startup.
[go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr/template) [go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr/template)
template template
* <a name="check_output_max_size"></a><a href="#check_output_max_size">`-check_output_max_size`</a> -
Override the default limit of 4k for maximum size of checks, this is a positive
value.
By limiting this size, it allows to put less pressure on Consul servers when
many checks are having a very large output in their checks.
In order to completely disable check output capture, it is possible to
use <a href="#discard_check_output">`discard_check_output`</a>.
* <a name="_client"></a><a href="#_client">`-client`</a> - The address to which * <a name="_client"></a><a href="#_client">`-client`</a> - The address to which
Consul will bind client interfaces, including the HTTP and DNS servers. By Consul will bind client interfaces, including the HTTP and DNS servers. By
default, this is "127.0.0.1", allowing only loopback connections. In Consul default, this is "127.0.0.1", allowing only loopback connections. In Consul
@ -457,6 +465,8 @@ will exit with an error at startup.
the Web UI resources for Consul. This will automatically enable the Web UI. The directory must be the Web UI resources for Consul. This will automatically enable the Web UI. The directory must be
readable to the agent. Starting with Consul version 0.7.0 and later, the Web UI assets are included in the binary so this flag is no longer necessary; specifying only the `-ui` flag is enough to enable the Web UI. Specifying both the '-ui' and '-ui-dir' flags will result in an error. readable to the agent. Starting with Consul version 0.7.0 and later, the Web UI assets are included in the binary so this flag is no longer necessary; specifying only the `-ui` flag is enough to enable the Web UI. Specifying both the '-ui' and '-ui-dir' flags will result in an error.
* <a name="_ui_content_path"></a><a href="#_ui_content_path">`-ui-content-path`</a> - This flag provides the option to change the path the Consul UI loads from and will be displayed in the browser. By default, the path is `/ui/`, for example `http://localhost:8500/ui/`. Only alphanumerics, `-`, and `_` are allowed in a custom path. `/v1/` is not allowed as it would overwrite the API endpoint.
## <a name="configuration_files"></a>Configuration Files ## <a name="configuration_files"></a>Configuration Files
In addition to the command-line options, configuration can be put into In addition to the command-line options, configuration can be put into

View File

@ -216,15 +216,15 @@ definition](/docs/connect/registration/service-registration.html) or
## Advanced Configuration ## Advanced Configuration
To support more flexibility when configuring Envoy, several "lower-level" options exist that To support more flexibility when configuring Envoy, several "lower-level" options exist
exist that require knowledge of Envoy's configuration format. that require knowledge of Envoy's configuration format.
Many allow configuring a subsection of either the bootstrap or Many options allow configuring a subsection of either the bootstrap or
dynamic configuration using your own custom protobuf config. dynamic configuration using your own custom protobuf config.
We separate these into two sets, [Advanced Bootstrap We separate these into two sets, [Advanced Bootstrap
Options](#advanced-bootstrap-options) and [Escape Hatch Options](#advanced-bootstrap-options) and [Escape Hatch
Overrides](#escape-hatch-overrides). Both require writing Envoy config in it's Overrides](#escape-hatch-overrides). Both require writing Envoy config in the
protobuf JSON encoding. Advanced options are smaller chunks that might protobuf JSON encoding. Advanced options cover smaller chunks that might
commonly need to be set for tasks like configuring tracing. In contrast, escape hatches commonly need to be set for tasks like configuring tracing. In contrast, escape hatches
give almost complete control over the proxy setup, but require operators to give almost complete control over the proxy setup, but require operators to
manually code the entire configuration in protobuf JSON. manually code the entire configuration in protobuf JSON.