Merge Consul OSS branch 'master' at commit 4eb73973b6e53336fd505dc727ac84c1f7e78872

This commit is contained in:
hashicorp-ci 2019-06-27 02:00:41 +00:00
commit 3224bea082
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: 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: 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:
@ -14,6 +17,7 @@ BUG FIXES:
* 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)]
* 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)]
## 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.CoordinateUpdateMaxBatches = a.config.ConsulCoordinateUpdateMaxBatches
base.CoordinateUpdatePeriod = a.config.ConsulCoordinateUpdatePeriod
base.CheckOutputMaxSize = a.config.CheckOutputMaxSize
base.RaftConfig.HeartbeatTimeout = a.config.ConsulRaftHeartbeatTimeout
base.RaftConfig.LeaderLeaseTimeout = a.config.ConsulRaftLeaderLeaseTimeout
@ -971,6 +972,9 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
if a.config.Bootstrap {
base.Bootstrap = true
}
if a.config.CheckOutputMaxSize > 0 {
base.CheckOutputMaxSize = a.config.CheckOutputMaxSize
}
if a.config.RejoinAfterLeave {
base.RejoinAfterLeave = true
}
@ -2248,6 +2252,13 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
// Check if already registered
if chkType != nil {
maxOutputSize := a.config.CheckOutputMaxSize
if maxOutputSize == 0 {
maxOutputSize = checks.DefaultBufSize
}
if chkType.OutputMaxSize > 0 && maxOutputSize > chkType.OutputMaxSize {
maxOutputSize = chkType.OutputMaxSize
}
switch {
case chkType.IsTTL():
@ -2261,6 +2272,7 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
CheckID: check.CheckID,
TTL: chkType.TTL,
Logger: a.logger,
OutputMaxSize: maxOutputSize,
}
// Restore persisted state, if any
@ -2294,6 +2306,7 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
Interval: chkType.Interval,
Timeout: chkType.Timeout,
Logger: a.logger,
OutputMaxSize: maxOutputSize,
TLSClientConfig: tlsClientConfig,
}
http.Start()
@ -2361,7 +2374,7 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
}
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 {
a.logger.Printf("[ERR] agent: error creating docker client: %s", err)
return err
@ -2396,7 +2409,6 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
check.CheckID, checks.MinInterval)
chkType.Interval = checks.MinInterval
}
monitor := &checks.CheckMonitor{
Notify: a.State,
CheckID: check.CheckID,
@ -2404,6 +2416,7 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType,
Interval: chkType.Interval,
Timeout: chkType.Timeout,
Logger: a.logger,
OutputMaxSize: maxOutputSize,
}
monitor.Start()
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.
check.SetStatus(status, output)
outputTruncated := check.SetStatus(status, output)
// We don't write any files in dev mode so bail here.
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
// 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)
}

View File

@ -19,7 +19,6 @@ import (
"github.com/hashicorp/consul/acl"
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/local"
"github.com/hashicorp/consul/agent/structs"
@ -725,12 +724,6 @@ func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Reques
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/"))
// Get the provided token, if any, and vet against any ACL policies.

View File

@ -18,7 +18,6 @@ import (
"time"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/config"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/debug"
@ -2299,7 +2298,8 @@ func TestAgent_FailCheck_ACLDeny(t *testing.T) {
func TestAgent_UpdateCheck(t *testing.T) {
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()
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) {
args := checkUpdate{
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))
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
// isn't super brittle.
state := a.State.Checks()["test"]
if state.Status != api.HealthPassing || len(state.Output) > 2*checks.BufSize {
t.Fatalf("bad: %v", state)
if state.Status != api.HealthPassing || len(state.Output) > 2*maxChecksSize {
t.Fatalf("bad: %v, (len:=%d)", state, len(state.Output))
}
})

View File

@ -1586,7 +1586,7 @@ func TestAgent_updateTTLCheck(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
checkBufSize := 100
health := &structs.HealthCheck{
Node: "foo",
CheckID: "mem",
@ -1595,6 +1595,7 @@ func TestAgent_updateTTLCheck(t *testing.T) {
}
chk := &structs.CheckType{
TTL: 15 * time.Second,
OutputMaxSize: checkBufSize,
}
// Add check and update it.
@ -1614,6 +1615,19 @@ func TestAgent_updateTTLCheck(t *testing.T) {
if status.Output != "foo" {
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) {

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.
MinInterval = time.Second
// BufSize is the maximum size of the captured
// check output. Prevents an enormous buffer
// DefaultBufSize is the maximum size of the captured
// check output by defaut. Prevents an enormous buffer
// from being captured
BufSize = 4 * 1024 // 4KB
DefaultBufSize = 4 * 1024 // 4KB
// UserAgent is the value of the User-Agent header
// for HTTP health checks.
@ -63,6 +63,7 @@ type CheckMonitor struct {
Interval time.Duration
Timeout time.Duration
Logger *log.Logger
OutputMaxSize int
stop bool
stopCh chan struct{}
@ -122,7 +123,7 @@ func (c *CheckMonitor) check() {
}
// Collect the output
output, _ := circbuf.NewBuffer(BufSize)
output, _ := circbuf.NewBuffer(int64(c.OutputMaxSize))
cmd.Stdout = output
cmd.Stderr = output
exec.SetSysProcAttr(cmd)
@ -222,12 +223,17 @@ type CheckTTL struct {
stop bool
stopCh chan struct{}
stopLock sync.Mutex
OutputMaxSize int
}
// Start is used to start a check ttl, runs until Stop()
func (c *CheckTTL) Start() {
c.stopLock.Lock()
defer c.stopLock.Unlock()
if c.OutputMaxSize < 1 {
c.OutputMaxSize = DefaultBufSize
}
c.stop = false
c.stopCh = make(chan struct{})
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,
// 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)
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)
// Store the last output so we can retain it if the TTL expires.
c.lastOutputLock.Lock()
c.lastOutput = output
c.lastOutputLock.Unlock()
c.timer.Reset(c.TTL)
return output
}
// CheckHTTP is used to periodically make an HTTP request to
@ -303,6 +315,7 @@ type CheckHTTP struct {
Timeout time.Duration
Logger *log.Logger
TLSClientConfig *tls.Config
OutputMaxSize int
httpClient *http.Client
stop bool
@ -339,6 +352,9 @@ func (c *CheckHTTP) Start() {
} else if c.Interval < 10*time.Second {
c.httpClient.Timeout = c.Interval
}
if c.OutputMaxSize < 1 {
c.OutputMaxSize = DefaultBufSize
}
}
c.stop = false
@ -413,7 +429,7 @@ func (c *CheckHTTP) check() {
defer resp.Body.Close()
// 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 {
c.Logger.Printf("[WARN] agent: Check %q error while reading body: %s", c.CheckID, err)
}

View File

@ -49,6 +49,7 @@ func TestCheckMonitor_Script(t *testing.T) {
Script: tt.script,
Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
}
check.Start()
defer check.Stop()
@ -84,6 +85,7 @@ func TestCheckMonitor_Args(t *testing.T) {
ScriptArgs: tt.args,
Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
}
check.Start()
defer check.Stop()
@ -109,6 +111,7 @@ func TestCheckMonitor_Timeout(t *testing.T) {
Interval: 50 * time.Millisecond,
Timeout: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
}
check.Start()
defer check.Stop()
@ -133,6 +136,7 @@ func TestCheckMonitor_RandomStagger(t *testing.T) {
ScriptArgs: []string{"sh", "-c", "exit 0"},
Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
}
check.Start()
defer check.Stop()
@ -158,6 +162,7 @@ func TestCheckMonitor_LimitOutput(t *testing.T) {
ScriptArgs: []string{"od", "-N", "81920", "/dev/urandom"},
Interval: 25 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
OutputMaxSize: DefaultBufSize,
}
check.Start()
defer check.Stop()
@ -165,7 +170,7 @@ func TestCheckMonitor_LimitOutput(t *testing.T) {
time.Sleep(50 * time.Millisecond)
// 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")
}
}
@ -287,7 +292,7 @@ func TestCheckHTTP(t *testing.T) {
}
// Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*BufSize)
body := bytes.Repeat([]byte{'a'}, 2*DefaultBufSize)
w.WriteHeader(tt.code)
w.Write(body)
}))
@ -299,6 +304,7 @@ func TestCheckHTTP(t *testing.T) {
CheckID: types.CheckID("foo"),
HTTP: server.URL,
Method: tt.method,
OutputMaxSize: DefaultBufSize,
Header: tt.header,
Interval: 10 * time.Millisecond,
Logger: log.New(ioutil.Discard, uniqueID(), log.LstdFlags),
@ -313,15 +319,52 @@ func TestCheckHTTP(t *testing.T) {
if got, want := notif.State("foo"), tt.status; got != want {
r.Fatalf("got state %q want %q", got, want)
}
// Allow slightly more data than BufSize, for the header
if n := len(notif.Output("foo")); n > (BufSize + 256) {
r.Fatalf("output too long: %d (%d-byte limit)", n, BufSize)
// Allow slightly more data than DefaultBufSize, for the header
if n := len(notif.Output("foo")); n > (DefaultBufSize + 256) {
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) {
t.Parallel()
timeout := 5 * time.Millisecond
@ -372,7 +415,7 @@ func TestCheckHTTP_disablesKeepAlives(t *testing.T) {
func largeBodyHandler(code int) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*BufSize)
body := bytes.Repeat([]byte{'a'}, 2*DefaultBufSize)
w.WriteHeader(code)
w.Write(body)
})

View File

@ -14,6 +14,7 @@ import (
"strings"
"time"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/connect/ca"
"github.com/hashicorp/consul/agent/consul"
"github.com/hashicorp/consul/agent/structs"
@ -786,6 +787,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
CAPath: b.stringVal(c.CAPath),
CertFile: b.stringVal(c.CertFile),
CheckUpdateInterval: b.durationVal("check_update_interval", c.CheckUpdateInterval),
CheckOutputMaxSize: b.intValWithDefault(c.CheckOutputMaxSize, 4096),
Checks: checks,
ClientAddrs: clientAddrs,
ConfigEntryBootstrap: configEntries,
@ -880,6 +882,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
TaggedAddresses: c.TaggedAddresses,
TranslateWANAddrs: b.boolVal(c.TranslateWANAddrs),
UIDir: b.stringVal(c.UIDir),
UIContentPath: UIPathBuilder(b.stringVal(b.Flags.Config.UIContentPath)),
UnixSocketGroup: b.stringVal(c.UnixSocket.Group),
UnixSocketMode: b.stringVal(c.UnixSocket.Mode),
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
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
//
@ -913,11 +919,20 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
return fmt.Errorf("datacenter cannot be empty")
}
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 {
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 {
fi, err := os.Stat(rt.DataDir)
switch {
@ -964,6 +979,9 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
if rt.BootstrapExpect > 0 && rt.Bootstrap {
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 {
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)
}
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 != "" {
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),
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),
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)
}
func (b *Builder) intVal(v *int) int {
func (b *Builder) intValWithDefault(v *int, defaultVal int) int {
if v == nil {
return 0
return defaultVal
}
return *v
}
func (b *Builder) intVal(v *int) int {
return b.intValWithDefault(v, 0)
}
func (b *Builder) portVal(name string, v *int) int {
if v == nil || *v <= 0 {
return -1
@ -1683,3 +1706,17 @@ func isUnixAddr(a net.Addr) bool {
_, ok := a.(*net.UnixAddr)
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"`
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
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"`
Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
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"`
TranslateWANAddrs *bool `json:"translate_wan_addrs,omitempty" hcl:"translate_wan_addrs" mapstructure:"translate_wan_addrs"`
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"`
UnixSocket UnixSocket `json:"unix_sockets,omitempty" hcl:"unix_sockets" mapstructure:"unix_sockets"`
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"`
Header map[string][]string `json:"header,omitempty" hcl:"header" mapstructure:"header"`
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"`
Interval *string `json:"interval,omitempty" hcl:"interval" mapstructure:"interval"`
DockerContainerID *string `json:"docker_container_id,omitempty" hcl:"docker_container_id" mapstructure:"docker_container_id"`

View File

@ -4,6 +4,7 @@ import (
"fmt"
"strconv"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/consul"
"github.com/hashicorp/consul/version"
)
@ -49,6 +50,7 @@ func DefaultSource() Source {
bind_addr = "0.0.0.0"
bootstrap = false
bootstrap_expect = 0
check_output_max_size = ` + strconv.Itoa(checks.DefaultBufSize) + `
check_update_interval = "5m"
client_addr = "127.0.0.1"
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.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.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-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'")
@ -105,6 +106,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
add(&f.Config.ServerMode, "server", "Switches agent to server mode.")
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.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.HCL, "hcl", "hcl config fragment. Can be specified multiple times.")
}

View File

@ -459,6 +459,11 @@ type RuntimeConfig struct {
// hcl: check_update_interval = "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.
//
// hcl: checks = [
@ -1382,6 +1387,10 @@ type RuntimeConfig struct {
// flag: -ui-dir 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
// Consul binds to UNIX sockets.
//

View File

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

View File

@ -7,6 +7,7 @@ import (
"os"
"time"
"github.com/hashicorp/consul/agent/checks"
"github.com/hashicorp/consul/agent/consul/autopilot"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/lib"
@ -368,6 +369,9 @@ type Config struct {
// warning and discard the remaining updates.
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.
// 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.
@ -502,6 +506,8 @@ func DefaultConfig() *Config {
CoordinateUpdateBatchSize: 128,
CoordinateUpdateMaxBatches: 5,
CheckOutputMaxSize: checks.DefaultBufSize,
RPCRate: rate.Inf,
RPCMaxBurst: 1000,

View File

@ -1363,6 +1363,17 @@ func (s *Server) handleAliveMember(member serf.Member) error {
ID: structs.ConsulServiceID,
Service: structs.ConsulServiceName,
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

View File

@ -1,17 +1,21 @@
package agent
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/pprof"
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"text/template"
"time"
"github.com/NYTimes/gziphandler"
@ -83,6 +87,66 @@ type HTTPServer struct {
// proto is filled by the agent to "http" or "https".
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 {
fs http.FileSystem
@ -96,6 +160,25 @@ func (fs *redirectFS) Open(name string) (http.File, error) {
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
// but returns a response object and error, both of which are handled in a
// common manner by Consul's HTTP server.
@ -207,7 +290,6 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
handleFuncMetrics(pattern, http.HandlerFunc(wrapper))
}
mux.HandleFunc("/", s.Index)
for pattern, fn := range endpoints {
thisFn := fn
@ -227,7 +309,6 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
if s.IsUIEnabled() {
var uifs http.FileSystem
// Use the custom UI dir if provided.
if 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()
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("/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
@ -489,7 +569,7 @@ func (s *HTTPServer) Index(resp http.ResponseWriter, req *http.Request) {
}
// 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

View File

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

View File

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

View File

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

View File

@ -974,7 +974,7 @@ function ui_version {
return 1
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"
return 0
}

View File

@ -2,7 +2,13 @@ ROOT:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
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
yarn run build
@ -19,6 +25,9 @@ test: deps
test-view: deps
yarn run test:view
test-parallel: deps
yarn test-parallel
lint: deps
yarn run lint:js
@ -31,4 +40,4 @@ steps:
node_modules: yarn.lock package.json
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>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "// all HTML content is populated via ./lib/startup/index"}}
{{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"}}
</head>
<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"}}
<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"}}
</body>
</html>

View File

@ -3,16 +3,17 @@
module.exports = {
name: 'startup',
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) {
case 'head':
return `<!-- CONSUL_VERSION: ${config.CONSUL_VERSION} -->`;
return require('./templates/head.html.js')(vars);
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"/>${
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>`;
return require('./templates/body.html.js')(vars);
case 'root-class':
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>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "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 "test-head-footer"}}
</head>
@ -22,10 +17,6 @@
{{content-for "test-body"}}
<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 "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"
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
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)
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
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
@ -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
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
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
To support more flexibility when configuring Envoy, several "lower-level" options exist that
exist that require knowledge of Envoy's configuration format.
Many allow configuring a subsection of either the bootstrap or
To support more flexibility when configuring Envoy, several "lower-level" options exist
that require knowledge of Envoy's configuration format.
Many options allow configuring a subsection of either the bootstrap or
dynamic configuration using your own custom protobuf config.
We separate these into two sets, [Advanced Bootstrap
Options](#advanced-bootstrap-options) and [Escape Hatch
Overrides](#escape-hatch-overrides). Both require writing Envoy config in it's
protobuf JSON encoding. Advanced options are smaller chunks that might
Overrides](#escape-hatch-overrides). Both require writing Envoy config in the
protobuf JSON encoding. Advanced options cover smaller chunks that might
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
manually code the entire configuration in protobuf JSON.