From cc4cdfc45ad5732d9490ea0c84ce9bf198a9a00f Mon Sep 17 00:00:00 2001 From: jbernat Date: Thu, 9 Nov 2017 14:43:30 +0100 Subject: [PATCH] implement_missing_parameters_healthcheck --- healthcheck/healthcheck.go | 46 ++++++++++++++++---- healthcheck/healthcheck_test.go | 74 ++++++++++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 13 deletions(-) diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index d047a77..446469d 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -190,8 +190,16 @@ func (c *Checker) checkHTTP(result chan bool) { return } - // Check status code: should be between 200 and 399 - if response.StatusCode < 200 || response.StatusCode > 399 { + // Check status code within the taskinfo statuses field + var customStatusFound = false + for _, customStatus := range c.TaskInfo.GetHealthCheck().GetHTTP().GetStatuses() { + if uint32(response.StatusCode) == customStatus { + customStatusFound = true + break + } + } + + if (response.StatusCode < 200 || response.StatusCode > 399) && !customStatusFound { logger.GetInstance().Error("Unexpected status code", zap.Int("code", response.StatusCode), ) @@ -244,19 +252,41 @@ func (c *Checker) checkTCP(result chan bool) { // checkCommand enters the container mount namespace and // executes the given command using the containerizer func (c *Checker) checkCommand(result chan bool) { - var cmd []string + wrapperCmd := []string{"su"} command := c.TaskInfo.GetHealthCheck().GetCommand() // Must be wrapped into a shell process if command.GetShell() { - cmd = []string{"/bin/sh", "-c", command.GetValue()} - } else { - cmd = []string{command.GetValue()} + wrapperCmd = append(wrapperCmd, []string{"-s", "/bin/sh"}...) } - + // Add user. Use root if no user specified + var user = command.GetUser() + if user == "" { + user = "root" + } + wrapperCmd = append(wrapperCmd, []string{"-", user}...) + // Construct the command that should be wrapped + var cmd []string + // Add environment variable + if len(command.GetEnvironment().GetVariables()) > 0 { + for _, envVar := range command.GetEnvironment().GetVariables() { + cmd = append([]string{envVar.Name + "=" + envVar.Value}, cmd...) + } + } + // Add command arguments + cmd = append(cmd, command.GetValue()) + if len(command.GetArguments()) > 0 { + for _, arg := range command.GetArguments() { + cmd = append(cmd, arg) + } + } + // Wrap the command + wrapperCmd = append(wrapperCmd, "-c \"") + wrapperCmd = append(wrapperCmd, cmd...) + wrapperCmd = append(wrapperCmd, "\"") // Prepare timeout context and all the stuff needed to call the containerizer ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.TaskInfo.GetHealthCheck().GetTimeoutSeconds())*time.Second) defer cancel() - execResult := c.Containerizer.ContainerExec(ctx, c.ContainerID, cmd) + execResult := c.Containerizer.ContainerExec(ctx, c.ContainerID, wrapperCmd) select { case err := <-execResult: // Done (with or without error) if err != nil { diff --git a/healthcheck/healthcheck_test.go b/healthcheck/healthcheck_test.go index c6f5956..c6c38a2 100644 --- a/healthcheck/healthcheck_test.go +++ b/healthcheck/healthcheck_test.go @@ -108,7 +108,7 @@ func (s *HealthcheckTestSuite) TestRun() { // Check that: // - Do not enter in network namespace if not in bridge network mode // - Result is false if server is not accessible -// - Result is false if HTTP code is not between 200 and 399 +// - Result is false if HTTP code is not between 200 and 399 and is not wihtin custom status code // - Result is true otherwise func (s *HealthcheckTestSuite) TestCheckHTTP() { // Prepare struct @@ -176,7 +176,20 @@ func (s *HealthcheckTestSuite) TestCheckHTTP() { port, _ = strconv.Atoi(portString[1]) s.taskInfo.HealthCheck.HTTP.Port = uint32(port) go s.checker.checkHTTP(s.result) - assert.Equal(s.T(), true, <-s.result) // Should be unhealthy + assert.Equal(s.T(), true, <-s.result) // Should be healthy + server.Close() + + // Result is ok with a custom status code + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) // 404 + })) + serverURL, _ = url.Parse(server.URL) + portString = strings.Split(serverURL.Host, ":") + port, _ = strconv.Atoi(portString[1]) + s.taskInfo.HealthCheck.HTTP.Statuses = []uint32{404} + s.taskInfo.HealthCheck.HTTP.Port = uint32(port) + go s.checker.checkHTTP(s.result) + assert.Equal(s.T(), true, <-s.result) // Should be healthy server.Close() } @@ -243,7 +256,7 @@ func (s *HealthcheckTestSuite) TestCheckTCP() { func (s *HealthcheckTestSuite) TestCheckCommand() { // Prepare struct shell := true - value := "sleep 1" + value := "ls" s.taskInfo.HealthCheck.Command = &mesos.CommandInfo{ Value: &value, Shell: &shell, @@ -265,13 +278,64 @@ func (s *HealthcheckTestSuite) TestCheckCommand() { // With wrapping go s.checker.checkCommand(s.result) <-s.result - assert.Equal(s.T(), []string{"/bin/sh", "-c", s.taskInfo.GetHealthCheck().GetCommand().GetValue()}, command) // Should be wrapped into a shell + assert.Equal( + s.T(), + []string{"su", "-s", "/bin/sh", "-", "root", "-c \"", s.taskInfo.GetHealthCheck().GetCommand().GetValue(), "\""}, + command, + ) // Should be wrapped into a shell // Without wrapping shell = false go s.checker.checkCommand(s.result) <-s.result - assert.Equal(s.T(), []string{s.taskInfo.GetHealthCheck().GetCommand().GetValue()}, command) // Should not be wrapped into a shell + assert.Equal( + s.T(), + []string{"su", "-", "root", "-c \"", s.taskInfo.GetHealthCheck().GetCommand().GetValue(), "\""}, + command) // Should not be wrapped into a shell + + //With arguments + s.taskInfo.HealthCheck.Command.Arguments = []string{"-l", "-i", "-a"} + expectedCommand := []string{"su", "-", "root", "-c \"", s.taskInfo.GetHealthCheck().GetCommand().GetValue()} + expectedCommand = append(expectedCommand, s.taskInfo.GetHealthCheck().GetCommand().GetArguments()...) + expectedCommand = append(expectedCommand, "\"") + go s.checker.checkCommand(s.result) + <-s.result + assert.Equal( + s.T(), + expectedCommand, + command, + ) + s.taskInfo.HealthCheck.Command.Arguments = []string{} + + //With environment variables + s.taskInfo.HealthCheck.Command.Environment = &mesos.Environment{ + Variables: []mesos.Environment_Variable{ + mesos.Environment_Variable{Name: "LOGLEVEL", Value: "DEBUG"}, + }, + } + go s.checker.checkCommand(s.result) + <-s.result + assert.Equal( + s.T(), + []string{"su", "-", "root", "-c \"", "LOGLEVEL=DEBUG", s.taskInfo.GetHealthCheck().GetCommand().GetValue(), "\""}, + command, + ) + s.taskInfo.HealthCheck.Command.Environment.Variables = []mesos.Environment_Variable{} + + //With specified user + user := "nobody" + s.taskInfo.HealthCheck.Command.User = &user + go s.checker.checkCommand(s.result) + <-s.result + assert.Equal( + s.T(), + []string{ + "su", "-", s.taskInfo.GetHealthCheck().GetCommand().GetUser(), + "-c \"", s.taskInfo.GetHealthCheck().GetCommand().GetValue(), "\"", + }, + command, + ) + user = "" // Nominal case go s.checker.checkCommand(s.result)