github.com/portworx/docker@v1.12.1/integration-cli/docker_cli_service_health_test.go (about)

     1  // +build !windows
     2  
     3  package main
     4  
     5  import (
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/docker/docker/daemon/cluster/executor/container"
    10  	"github.com/docker/docker/pkg/integration/checker"
    11  	"github.com/docker/engine-api/types/swarm"
    12  	"github.com/go-check/check"
    13  )
    14  
    15  // start a service, and then make its task unhealthy during running
    16  // finally, unhealthy task should be detected and killed
    17  func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) {
    18  	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
    19  
    20  	d := s.AddDaemon(c, true, true)
    21  
    22  	// build image with health-check
    23  	// note: use `daemon.buildImageWithOut` to build, do not use `buildImage` to build
    24  	imageName := "testhealth"
    25  	_, _, err := d.buildImageWithOut(imageName,
    26  		`FROM busybox
    27  		RUN touch /status
    28  		HEALTHCHECK --interval=1s --timeout=1s --retries=1\
    29  		  CMD cat /status`,
    30  		true)
    31  	c.Check(err, check.IsNil)
    32  
    33  	serviceName := "healthServiceRun"
    34  	out, err := d.Cmd("service", "create", "--name", serviceName, imageName, "top")
    35  	c.Assert(err, checker.IsNil, check.Commentf(out))
    36  	id := strings.TrimSpace(out)
    37  
    38  	var tasks []swarm.Task
    39  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
    40  		tasks = d.getServiceTasks(c, id)
    41  		return tasks, nil
    42  	}, checker.HasLen, 1)
    43  
    44  	task := tasks[0]
    45  
    46  	// wait for task to start
    47  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
    48  		task = d.getTask(c, task.ID)
    49  		return task.Status.State, nil
    50  	}, checker.Equals, swarm.TaskStateStarting)
    51  	containerID := task.Status.ContainerStatus.ContainerID
    52  
    53  	// wait for container to be healthy
    54  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
    55  		out, _ := d.Cmd("inspect", "--format={{.State.Health.Status}}", containerID)
    56  		return strings.TrimSpace(out), nil
    57  	}, checker.Equals, "healthy")
    58  
    59  	// make it fail
    60  	d.Cmd("exec", containerID, "rm", "/status")
    61  	// wait for container to be unhealthy
    62  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
    63  		out, _ := d.Cmd("inspect", "--format={{.State.Health.Status}}", containerID)
    64  		return strings.TrimSpace(out), nil
    65  	}, checker.Equals, "unhealthy")
    66  
    67  	// Task should be terminated
    68  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
    69  		task = d.getTask(c, task.ID)
    70  		return task.Status.State, nil
    71  	}, checker.Equals, swarm.TaskStateFailed)
    72  
    73  	if !strings.Contains(task.Status.Err, container.ErrContainerUnhealthy.Error()) {
    74  		c.Fatal("unhealthy task exits because of other error")
    75  	}
    76  }
    77  
    78  // start a service whose task is unhealthy at beginning
    79  // its tasks should be blocked in starting stage, until health check is passed
    80  func (s *DockerSwarmSuite) TestServiceHealthStart(c *check.C) {
    81  	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
    82  
    83  	d := s.AddDaemon(c, true, true)
    84  
    85  	// service started from this image won't pass health check
    86  	imageName := "testhealth"
    87  	_, _, err := d.buildImageWithOut(imageName,
    88  		`FROM busybox
    89  		HEALTHCHECK --interval=1s --timeout=1s --retries=1024\
    90  		  CMD cat /status`,
    91  		true)
    92  	c.Check(err, check.IsNil)
    93  
    94  	serviceName := "healthServiceStart"
    95  	out, err := d.Cmd("service", "create", "--name", serviceName, imageName, "top")
    96  	c.Assert(err, checker.IsNil, check.Commentf(out))
    97  	id := strings.TrimSpace(out)
    98  
    99  	var tasks []swarm.Task
   100  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   101  		tasks = d.getServiceTasks(c, id)
   102  		return tasks, nil
   103  	}, checker.HasLen, 1)
   104  
   105  	task := tasks[0]
   106  
   107  	// wait for task to start
   108  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   109  		task = d.getTask(c, task.ID)
   110  		return task.Status.State, nil
   111  	}, checker.Equals, swarm.TaskStateStarting)
   112  
   113  	containerID := task.Status.ContainerStatus.ContainerID
   114  
   115  	// wait for health check to work
   116  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   117  		out, _ := d.Cmd("inspect", "--format={{.State.Health.FailingStreak}}", containerID)
   118  		failingStreak, _ := strconv.Atoi(strings.TrimSpace(out))
   119  		return failingStreak, nil
   120  	}, checker.GreaterThan, 0)
   121  
   122  	// task should be blocked at starting status
   123  	task = d.getTask(c, task.ID)
   124  	c.Assert(task.Status.State, check.Equals, swarm.TaskStateStarting)
   125  
   126  	// make it healthy
   127  	d.Cmd("exec", containerID, "touch", "/status")
   128  
   129  	// Task should be at running status
   130  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   131  		task = d.getTask(c, task.ID)
   132  		return task.Status.State, nil
   133  	}, checker.Equals, swarm.TaskStateRunning)
   134  }
   135  
   136  // start a service whose task is unhealthy at beginning
   137  // its tasks should be blocked in starting stage, until health check is passed
   138  func (s *DockerSwarmSuite) TestServiceHealthUpdate(c *check.C) {
   139  	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
   140  
   141  	d := s.AddDaemon(c, true, true)
   142  
   143  	// service started from this image won't pass health check
   144  	imageName := "testhealth"
   145  	_, _, err := d.buildImageWithOut(imageName,
   146  		`FROM busybox
   147  		HEALTHCHECK --interval=1s --timeout=1s --retries=1024\
   148  		  CMD cat /status`,
   149  		true)
   150  	c.Check(err, check.IsNil)
   151  
   152  	serviceName := "healthServiceStart"
   153  	out, err := d.Cmd("service", "create", "--name", serviceName, imageName, "top")
   154  	c.Assert(err, checker.IsNil, check.Commentf(out))
   155  	id := strings.TrimSpace(out)
   156  
   157  	var tasks []swarm.Task
   158  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   159  		tasks = d.getServiceTasks(c, id)
   160  		return tasks, nil
   161  	}, checker.HasLen, 1)
   162  
   163  	task := tasks[0]
   164  
   165  	// wait for task to start
   166  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   167  		task = d.getTask(c, task.ID)
   168  		return task.Status.State, nil
   169  	}, checker.Equals, swarm.TaskStateStarting)
   170  
   171  	containerID := task.Status.ContainerStatus.ContainerID
   172  
   173  	// wait for health check to work
   174  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   175  		out, _ := d.Cmd("inspect", "--format={{.State.Health.FailingStreak}}", containerID)
   176  		failingStreak, _ := strconv.Atoi(strings.TrimSpace(out))
   177  		return failingStreak, nil
   178  	}, checker.GreaterThan, 0)
   179  
   180  	// task should be blocked at starting status
   181  	task = d.getTask(c, task.ID)
   182  	c.Assert(task.Status.State, check.Equals, swarm.TaskStateStarting)
   183  
   184  	// make it healthy
   185  	d.Cmd("exec", containerID, "touch", "/status")
   186  	// Task should be at running status
   187  	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
   188  		task = d.getTask(c, task.ID)
   189  		return task.Status.State, nil
   190  	}, checker.Equals, swarm.TaskStateRunning)
   191  }