Merge pull request #1424 from hashicorp/b-docker-client-api

Adds Docker checks support to client API.
This commit is contained in:
James Phillips 2015-11-18 09:13:54 -08:00
commit 1a6718d9b2
8 changed files with 72 additions and 20 deletions

View File

@ -63,13 +63,15 @@ type AgentCheckRegistration struct {
// AgentServiceCheck is used to create an associated // AgentServiceCheck is used to create an associated
// check for a service // check for a service
type AgentServiceCheck struct { type AgentServiceCheck struct {
Script string `json:",omitempty"` Script string `json:",omitempty"`
Interval string `json:",omitempty"` DockerContainerID string `json:",omitempty"`
Timeout string `json:",omitempty"` Shell string `json:",omitempty"` // Only supported for Docker.
TTL string `json:",omitempty"` Interval string `json:",omitempty"`
HTTP string `json:",omitempty"` Timeout string `json:",omitempty"`
TCP string `json:",omitempty"` TTL string `json:",omitempty"`
Status string `json:",omitempty"` HTTP string `json:",omitempty"`
TCP string `json:",omitempty"`
Status string `json:",omitempty"`
} }
type AgentServiceChecks []*AgentServiceCheck type AgentServiceChecks []*AgentServiceCheck

View File

@ -387,6 +387,50 @@ func TestAgent_Checks_serviceBound(t *testing.T) {
} }
} }
func TestAgent_Checks_Docker(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()
agent := c.Agent()
// First register a service
serviceReg := &AgentServiceRegistration{
Name: "redis",
}
if err := agent.ServiceRegister(serviceReg); err != nil {
t.Fatalf("err: %v", err)
}
// Register a check bound to the service
reg := &AgentCheckRegistration{
Name: "redischeck",
ServiceID: "redis",
AgentServiceCheck: AgentServiceCheck{
DockerContainerID: "f972c95ebf0e",
Script: "/bin/true",
Shell: "/bin/bash",
Interval: "10s",
},
}
if err := agent.CheckRegister(reg); err != nil {
t.Fatalf("err: %v", err)
}
checks, err := agent.Checks()
if err != nil {
t.Fatalf("err: %v", err)
}
check, ok := checks["redischeck"]
if !ok {
t.Fatalf("missing check: %v", checks)
}
if check.ServiceID != "redis" {
t.Fatalf("missing service association for check: %v", check)
}
}
func TestAgent_Join(t *testing.T) { func TestAgent_Join(t *testing.T) {
t.Parallel() t.Parallel()
c, s := makeClient(t) c, s := makeClient(t)

View File

@ -919,7 +919,7 @@ func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *CheckType, persist
dockerCheck := &CheckDocker{ dockerCheck := &CheckDocker{
Notify: &a.state, Notify: &a.state,
CheckID: check.CheckID, CheckID: check.CheckID,
DockerContainerId: chkType.DockerContainerId, DockerContainerID: chkType.DockerContainerID,
Shell: chkType.Shell, Shell: chkType.Shell,
Script: chkType.Script, Script: chkType.Script,
Interval: chkType.Interval, Interval: chkType.Interval,

View File

@ -44,7 +44,7 @@ type CheckType struct {
HTTP string HTTP string
TCP string TCP string
Interval time.Duration Interval time.Duration
DockerContainerId string DockerContainerID string
Shell string Shell string
Timeout time.Duration Timeout time.Duration
@ -68,7 +68,7 @@ func (c *CheckType) IsTTL() bool {
// IsMonitor checks if this is a Monitor type // IsMonitor checks if this is a Monitor type
func (c *CheckType) IsMonitor() bool { func (c *CheckType) IsMonitor() bool {
return c.Script != "" && c.DockerContainerId == "" && c.Interval != 0 return c.Script != "" && c.DockerContainerID == "" && c.Interval != 0
} }
// IsHTTP checks if this is a HTTP type // IsHTTP checks if this is a HTTP type
@ -82,7 +82,7 @@ func (c *CheckType) IsTCP() bool {
} }
func (c *CheckType) IsDocker() bool { func (c *CheckType) IsDocker() bool {
return c.DockerContainerId != "" && c.Script != "" && c.Interval != 0 return c.DockerContainerID != "" && c.Script != "" && c.Interval != 0
} }
// CheckNotifier interface is used by the CheckMonitor // CheckNotifier interface is used by the CheckMonitor
@ -518,7 +518,7 @@ type CheckDocker struct {
Notify CheckNotifier Notify CheckNotifier
CheckID string CheckID string
Script string Script string
DockerContainerId string DockerContainerID string
Shell string Shell string
Interval time.Duration Interval time.Duration
Logger *log.Logger Logger *log.Logger
@ -574,7 +574,7 @@ func (c *CheckDocker) Stop() {
func (c *CheckDocker) run() { func (c *CheckDocker) run() {
// Get the randomized initial pause time // Get the randomized initial pause time
initialPauseTime := randomStagger(c.Interval) initialPauseTime := randomStagger(c.Interval)
c.Logger.Printf("[DEBUG] agent: pausing %v before first invocation of %s -c %s in container %s", initialPauseTime, c.Shell, c.Script, c.DockerContainerId) c.Logger.Printf("[DEBUG] agent: pausing %v before first invocation of %s -c %s in container %s", initialPauseTime, c.Shell, c.Script, c.DockerContainerID)
next := time.After(initialPauseTime) next := time.After(initialPauseTime)
for { for {
select { select {
@ -595,7 +595,7 @@ func (c *CheckDocker) check() {
AttachStderr: true, AttachStderr: true,
Tty: false, Tty: false,
Cmd: c.cmd, Cmd: c.cmd,
Container: c.DockerContainerId, Container: c.DockerContainerID,
} }
var ( var (
exec *docker.Exec exec *docker.Exec

View File

@ -535,7 +535,7 @@ func expectDockerCheckStatus(t *testing.T, dockerClient DockerClient, status str
Notify: mock, Notify: mock,
CheckID: "foo", CheckID: "foo",
Script: "/health.sh", Script: "/health.sh",
DockerContainerId: "54432bad1fc7", DockerContainerID: "54432bad1fc7",
Shell: "/bin/sh", Shell: "/bin/sh",
Interval: 10 * time.Millisecond, Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, "", log.LstdFlags), Logger: log.New(os.Stderr, "", log.LstdFlags),
@ -595,7 +595,7 @@ func TestDockerCheckDefaultToSh(t *testing.T) {
Notify: mock, Notify: mock,
CheckID: "foo", CheckID: "foo",
Script: "/health.sh", Script: "/health.sh",
DockerContainerId: "54432bad1fc7", DockerContainerID: "54432bad1fc7",
Interval: 10 * time.Millisecond, Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, "", log.LstdFlags), Logger: log.New(os.Stderr, "", log.LstdFlags),
dockerClient: &fakeDockerClientWithNoErrors{}, dockerClient: &fakeDockerClientWithNoErrors{},
@ -620,7 +620,7 @@ func TestDockerCheckUseShellFromEnv(t *testing.T) {
Notify: mock, Notify: mock,
CheckID: "foo", CheckID: "foo",
Script: "/health.sh", Script: "/health.sh",
DockerContainerId: "54432bad1fc7", DockerContainerID: "54432bad1fc7",
Interval: 10 * time.Millisecond, Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, "", log.LstdFlags), Logger: log.New(os.Stderr, "", log.LstdFlags),
dockerClient: &fakeDockerClientWithNoErrors{}, dockerClient: &fakeDockerClientWithNoErrors{},
@ -645,7 +645,7 @@ func TestDockerCheckTruncateOutput(t *testing.T) {
Notify: mock, Notify: mock,
CheckID: "foo", CheckID: "foo",
Script: "/health.sh", Script: "/health.sh",
DockerContainerId: "54432bad1fc7", DockerContainerID: "54432bad1fc7",
Shell: "/bin/sh", Shell: "/bin/sh",
Interval: 10 * time.Millisecond, Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, "", log.LstdFlags), Logger: log.New(os.Stderr, "", log.LstdFlags),

View File

@ -782,7 +782,7 @@ func FixupCheckType(raw interface{}) error {
rawMap["serviceid"] = v rawMap["serviceid"] = v
delete(rawMap, "service_id") delete(rawMap, "service_id")
case "docker_container_id": case "docker_container_id":
rawMap["DockerContainerId"] = v rawMap["DockerContainerID"] = v
delete(rawMap, "docker_container_id") delete(rawMap, "docker_container_id")
} }
} }

View File

@ -1133,7 +1133,7 @@ func TestDecodeConfig_Check(t *testing.T) {
t.Fatalf("bad: %v", chk) t.Fatalf("bad: %v", chk)
} }
if chk.DockerContainerId != "redis" { if chk.DockerContainerID != "redis" {
t.Fatalf("bad: %v", chk) t.Fatalf("bad: %v", chk)
} }
} }

View File

@ -241,6 +241,8 @@ body must look like:
"Name": "Memory utilization", "Name": "Memory utilization",
"Notes": "Ensure we don't oversubscribe memory", "Notes": "Ensure we don't oversubscribe memory",
"Script": "/usr/local/bin/check_mem.py", "Script": "/usr/local/bin/check_mem.py",
"DockerContainerID": "f972c95ebf0e",
"Shell": "/bin/bash",
"HTTP": "http://example.com", "HTTP": "http://example.com",
"TCP": "example.com:22", "TCP": "example.com:22",
"Interval": "10s", "Interval": "10s",
@ -259,6 +261,10 @@ The `Notes` field is not used internally by Consul and is meant to be human-read
If a `Script` is provided, the check type is a script, and Consul will If a `Script` is provided, the check type is a script, and Consul will
evaluate the script every `Interval` to update the status. evaluate the script every `Interval` to update the status.
If a `DockerContainerID` is provided, the check is a Docker check, and Consul will
evaluate the script every `Interval` in the given container using the specified
`Shell`. Note that `Shell` is currently only supported for Docker checks.
An `HTTP` check will perform an HTTP GET request against the value of `HTTP` (expected to An `HTTP` check will perform an HTTP GET request against the value of `HTTP` (expected to
be a URL) every `Interval`. If the response is any `2xx` code, the check is `passing`. be a URL) every `Interval`. If the response is any `2xx` code, the check is `passing`.
If the response is `429 Too Many Requests`, the check is `warning`. Otherwise, the check If the response is `429 Too Many Requests`, the check is `warning`. Otherwise, the check