diff --git a/checks/docker.go b/checks/docker.go index 2d4e26c..69f6bfb 100644 --- a/checks/docker.go +++ b/checks/docker.go @@ -1,38 +1,41 @@ package checks import ( + "fmt" + "os" + "regexp" + "strings" + "time" + "github.com/CiscoCloud/distributive/chkutil" "github.com/CiscoCloud/distributive/dockerstatus" "github.com/CiscoCloud/distributive/errutil" "github.com/CiscoCloud/distributive/tabular" log "github.com/Sirupsen/logrus" "github.com/fsouza/go-dockerclient" - "os" - "regexp" - "strings" ) /* #### DockerImage Description: Is this Docker image present? Parameters: - - Name (string): Name of the image +- Name (string): Name of the image Example parameters: - - "user/image", "ubuntu" +- "user/image", "ubuntu" */ type DockerImage struct{ name string } -func init() { - chkutil.Register("DockerImage", func() chkutil.Check { - return &DockerImage{} - }) - chkutil.Register("DockerRunning", func() chkutil.Check { - return &DockerRunning{} - }) - chkutil.Register("DockerRunningRegext", func() chkutil.Check { - return &DockerRunningRegexp{} - }) +func init() { + chkutil.Register("DockerImage", func() chkutil.Check { + return &DockerImage{} + }) + chkutil.Register("DockerRunning", func() chkutil.Check { + return &DockerRunning{} + }) + chkutil.Register("DockerRunningRegext", func() chkutil.Check { + return &DockerRunningRegexp{} + }) } func (chk DockerImage) New(params []string) (chkutil.Check, error) { @@ -92,9 +95,9 @@ func (chk DockerImageRegexp) Status() (int, string, error) { #### DockerRunning Description: Is this Docker container running? Parameters: - - Name (string): Name of the container +- Name (string): Name of the container Example parameters: - - "user/container", "user/container:latest" +- "user/container", "user/container:latest" */ type DockerRunning struct{ name string } @@ -150,11 +153,11 @@ func getRunningContainersAPI(endpoint string) (containers []string) { Description: Works like DockerRunning, but fetches information from the Docker API endpoint instead. Parameters: - - Path (filepath): Path to Docker socket - - Name (string): Name of the container +- Path (filepath): Path to Docker socket +- Name (string): Name of the container Example parameters: - - "/var/run/docker.sock", "/path/to/docker.sock" - - "user/container", "user/container:latest" +- "/var/run/docker.sock", "/path/to/docker.sock" +- "user/container", "user/container:latest" */ type DockerRunningAPI struct{ path, name string } @@ -186,9 +189,9 @@ func (chk DockerRunningAPI) Status() (int, string, error) { Description: Works like DockerRunning, but matches with a regexp instead of a string. Parameters: - - Regexp (regexp): Regexp to match names with +- Regexp (regexp): Regexp to match names with Example parameters: - - "user/.+", "user/[cC](o){2,3}[nta]tai\w{2}r" +- "user/.+", "user/[cC](o){2,3}[nta]tai\w{2}r" */ type DockerRunningRegexp struct{ re *regexp.Regexp } @@ -215,3 +218,34 @@ func (chk DockerRunningRegexp) Status() (int, string, error) { msg := "Docker container not runnning" return errutil.GenericError(msg, chk.re.String(), running) } + +/* +#### DockerDaemonTimeout +Description: Tests if the Docker Daemon responds within a given timeout. +Parameters: +- Timeout (time.Duration) +Example parameters: +- "5s" +- "2m" +*/ +type DockerDaemonTimeout struct{ timeout time.Duration } + +func (chk DockerDaemonTimeout) New(params []string) (chkutil.Check, error) { + if len(params) != 1 { + return chk, errutil.ParameterLengthError{1, params} + } + timeout, err := time.ParseDuration(params[0]) + if err != nil { + return chk, errutil.ParameterTypeError{params[0], "time.Duration"} + } + chk.timeout = timeout + return chk, nil +} + +func (chk DockerDaemonTimeout) Status() (int, string, error) { + err := dockerstatus.DaemonResponding(chk.timeout) + if err == nil { + return errutil.Success() + } + return 1, fmt.Sprintf("Docker daemon isn't responding: %v", err), nil +} diff --git a/checks/docker_test.go b/checks/docker_test.go index 9e23abb..1f26745 100644 --- a/checks/docker_test.go +++ b/checks/docker_test.go @@ -5,10 +5,10 @@ import ( ) func TestDockerImage(t *testing.T) { + t.Parallel() if testing.Short() { t.Skip("Skipping docker tests in short mode") } else { - t.Parallel() validInputs := names invalidInputs := notLengthOne // inputs that should lead to success @@ -22,10 +22,10 @@ func TestDockerImage(t *testing.T) { } func TestDockerImageRegexp(t *testing.T) { + t.Parallel() if testing.Short() { t.Skip("Skipping docker tests in short mode") } else { - t.Parallel() validInputs := [][]string{ {"name"}, {"test*"}, {`win\d{1}`}, } @@ -41,10 +41,10 @@ func TestDockerImageRegexp(t *testing.T) { } func TestDockerRunning(t *testing.T) { + t.Parallel() if testing.Short() { t.Skip("Skipping docker tests in short mode") } else { - t.Parallel() validInputs := names invalidInputs := notLengthOne goodEggs := [][]string{} @@ -55,36 +55,11 @@ func TestDockerRunning(t *testing.T) { } } -/* -func TestDockerRunningAPI(t *testing.T) { - if testing.Short() { - t.Skip("Skipping docker tests in short mode") - } else { - t.Parallel() - validInputs := [][]string{ - {"/var/run/docker.sock", "name"}, - {"/var/run/docker.sock", "test"}, - {"/var/run/docker.sock", "win"}, - } - invalidInputs := notLengthOne - invalidInputs = append(invalidInputs, names...) - goodEggs := [][]string{} - badEggs := [][]string{ - {"/var/run/docker.sock", "failme"}, - {"/var/run/docker.sock", "fail"}, - {"/var/run/docker.sock", "loser"}, - } - testParameters(validInputs, invalidInputs, DockerRunningAPI{}, t) - testCheck(goodEggs, badEggs, DockerRunningAPI{}, t) - } -} -*/ - func TestDockerRunningRegexp(t *testing.T) { + t.Parallel() if testing.Short() { t.Skip("Skipping docker tests in short mode") } else { - t.Parallel() validInputs := names // TODO invalid regexps invalidInputs := notLengthOne @@ -95,3 +70,16 @@ func TestDockerRunningRegexp(t *testing.T) { testCheck(goodEggs, badEggs, DockerRunning{}, t) } } + +func TestDockerDaemonTimeout(t *testing.T) { + t.Parallel() + if testing.Short() { + t.Skip("Skipping docker tests in short mode") + } else { + validInputs := [][]string{{"5s"}, {"0m"}, {"1h"}} + invalidInputs := append(notLengthOne, []string{"1fail", "sec"}) + badEggs := [][]string{{"0s"}, {".1μs"}} + testParameters(validInputs, invalidInputs, DockerDaemonTimeout{}, t) + testCheck([][]string{}, badEggs, DockerDaemonTimeout{}, t) + } +} diff --git a/chkutil/chkutil.go b/chkutil/chkutil.go index e9ed330..0d63a68 100644 --- a/chkutil/chkutil.go +++ b/chkutil/chkutil.go @@ -92,11 +92,13 @@ func CommandTimeout(cmd *exec.Cmd, timeout time.Duration) (string, error) { case cmdOutput := <-out: return cmdOutput.Out, cmdOutput.Err case <-timedOut: - err := cmd.Process.Kill() - if err != nil { - return "", fmt.Errorf("Error while killing timed out process %v: %v", cmd, err) + if cmd != nil && cmd.Process != nil { + err := cmd.Process.Kill() + if err != nil { + return "", fmt.Errorf("Error while killing timed out process %v: %v", cmd, err) + } } - return "", fmt.Errorf("Command timed out: %v", cmd) + return "", fmt.Errorf("cmd's Process pointer was nil: %v", cmd) } } diff --git a/dockerstatus/dockerstatus.go b/dockerstatus/dockerstatus.go index d7dbba0..635f194 100644 --- a/dockerstatus/dockerstatus.go +++ b/dockerstatus/dockerstatus.go @@ -66,3 +66,10 @@ func RunningContainers() (containers []string, err error) { } return parseRunningContainers(string(out)), nil } + +// DaemonResponding checks to see if the Docker daemon responds to commands +// within the given timeout. If everything goes well, it returns nil. +func DaemonResponding(timeout time.Duration) error { + _, err := chkutil.CommandTimeout(exec.Command("docker", "ps"), timeout) + return err +}