Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Add healthcheck #112

Merged
merged 4 commits into from Oct 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 26 additions & 0 deletions application/service/docker/docker.go
@@ -0,0 +1,26 @@
package docker

import (
"context"
"github.com/duck8823/duci/infrastructure/docker"
"github.com/pkg/errors"
)

type Service interface {
Status() error
}

type serviceImpl struct {
moby docker.Client
}

func New(moby docker.Client) Service {
return &serviceImpl{moby}
}

func (s *serviceImpl) Status() error {
if _, err := s.moby.Info(context.Background()); err != nil {
return errors.Wrap(err, "Couldn't connect to Docker daemon.")
}
return nil
}
45 changes: 45 additions & 0 deletions application/service/docker/mock_docker/docker.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions infrastructure/docker/docker.go
Expand Up @@ -50,6 +50,7 @@ type Client interface {
Rm(ctx context.Context, containerID string) error
Rmi(ctx context.Context, tag string) error
ExitCode(ctx context.Context, containerID string) (int64, error)
Info(ctx context.Context) (types.Info, error)
}

type clientImpl struct {
Expand Down Expand Up @@ -136,3 +137,12 @@ func (c *clientImpl) ExitCode(ctx context.Context, containerID string) (int64, e
return -1, errors.WithStack(e)
}
}

// Status returns error when failure get docker status.
func (c *clientImpl) Info(ctx context.Context) (types.Info, error) {
info, err := c.moby.Info(ctx)
if err != nil {
return types.Info{}, errors.WithStack(err)
}
return info, nil
}
53 changes: 53 additions & 0 deletions infrastructure/docker/docker_test.go
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/duck8823/duci/infrastructure/docker"
"github.com/duck8823/duci/infrastructure/docker/mock_docker"
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/labstack/gommon/random"
"github.com/pkg/errors"
Expand Down Expand Up @@ -464,6 +465,58 @@ func TestClientImpl_ExitCode(t *testing.T) {
})
}

func TestClientImpl_Info(t *testing.T) {
// setup
sut, err := docker.New()
if err != nil {
t.Fatalf("error occurred: %+v", err)
}

t.Run("without error", func(t *testing.T) {
// given
expected := types.Info{ID: uuid.New().String()}

// and
ctrl := gomock.NewController(t)
mockMoby := mock_docker.NewMockMoby(ctrl)

mockMoby.EXPECT().
Info(gomock.Any()).
Return(expected, nil)

sut.SetMoby(mockMoby)

// when
actual, err := sut.Info(context.New("test", uuid.New(), nil))

// then
if !cmp.Equal(actual, expected) {
t.Errorf("must be equal. %+v", cmp.Diff(actual, expected))
}

if err != nil {
t.Errorf("error must not occur, but got %+v", err)
}
})

t.Run("with error", func(t *testing.T) {
// given
ctrl := gomock.NewController(t)
mockMoby := mock_docker.NewMockMoby(ctrl)

mockMoby.EXPECT().
Info(gomock.Any()).
Return(types.Info{}, errors.New("test"))

sut.SetMoby(mockMoby)

// expect
if _, err := sut.Info(context.New("test", uuid.New(), nil)); err == nil {
t.Error("error must occur, but got nil")
}
})
}

func TestEnvironments_ToArray(t *testing.T) {
var empty []string
for _, testcase := range []struct {
Expand Down
14 changes: 14 additions & 0 deletions infrastructure/docker/mock_docker/docker.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions infrastructure/docker/mock_docker/third_party.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions infrastructure/docker/third_pirty.go
Expand Up @@ -48,4 +48,7 @@ type Moby interface {
containerID string,
condition container.WaitCondition,
) (<-chan container.ContainerWaitOKBody, <-chan error)
Info(
ctx context.Context,
) (types.Info, error)
}
20 changes: 20 additions & 0 deletions presentation/controller/health.go
@@ -0,0 +1,20 @@
package controller

import (
"github.com/duck8823/duci/application/service/docker"
"net/http"
)

// HealthController is a handler of health check.
type HealthController struct {
Docker docker.Service
}

// ServeHTTP responses a server status
func (c *HealthController) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := c.Docker.Status(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
71 changes: 71 additions & 0 deletions presentation/controller/health_test.go
@@ -0,0 +1,71 @@
package controller_test

import (
"github.com/duck8823/duci/application/service/docker/mock_docker"
"github.com/duck8823/duci/presentation/controller"
"github.com/golang/mock/gomock"
"github.com/pkg/errors"
"net/http/httptest"
"testing"
)

func TestHealthCheckController_ServeHTTP(t *testing.T) {
t.Run("without error", func(t *testing.T) {
// given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockService := mock_docker.NewMockService(ctrl)
mockService.EXPECT().
Status().
Return(nil)

handler := &controller.HealthController{Docker: mockService}

// and
s := httptest.NewServer(handler)
defer s.Close()

// and
req := httptest.NewRequest("GET", "/health", nil)
rec := httptest.NewRecorder()

// when
handler.ServeHTTP(rec, req)

// then
if rec.Code != 200 {
t.Errorf("status code must be 200, but got %+v", rec.Code)
}
})

t.Run("with error", func(t *testing.T) {
// given
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockService := mock_docker.NewMockService(ctrl)
mockService.EXPECT().
Status().
Return(errors.New("test"))

handler := &controller.HealthController{Docker: mockService}

// and
s := httptest.NewServer(handler)
defer s.Close()

// and
req := httptest.NewRequest("GET", "/health", nil)
rec := httptest.NewRecorder()

// when
handler.ServeHTTP(rec, req)

// then
if rec.Code != 500 {
t.Errorf("status code must be 500, but got %+v", rec.Code)
}
})

}
18 changes: 11 additions & 7 deletions presentation/router/router.go
Expand Up @@ -2,11 +2,12 @@ package router

import (
"github.com/duck8823/duci/application"
"github.com/duck8823/duci/application/service/docker"
"github.com/duck8823/duci/application/service/git"
"github.com/duck8823/duci/application/service/github"
"github.com/duck8823/duci/application/service/logstore"
"github.com/duck8823/duci/application/service/runner"
"github.com/duck8823/duci/infrastructure/docker"
moby "github.com/duck8823/duci/infrastructure/docker"
"github.com/duck8823/duci/presentation/controller"
"github.com/go-chi/chi"
"github.com/pkg/errors"
Expand All @@ -15,22 +16,29 @@ import (

// New returns handler of application.
func New() (http.Handler, error) {
dockerClient, err := moby.New()
if err != nil {
return nil, errors.WithStack(err)
}

logstoreService, githubService, err := createCommonServices()
if err != nil {
return nil, errors.WithStack(err)
}

dockerRunner, err := createRunner(logstoreService, githubService)
dockerRunner, err := createRunner(logstoreService, githubService, dockerClient)
if err != nil {
return nil, errors.WithStack(err)
}

webhooksCtrl := &controller.WebhooksController{Runner: dockerRunner, GitHub: githubService}
logCtrl := &controller.LogController{LogStore: logstoreService}
healthCtrl := &controller.HealthController{Docker: docker.New(dockerClient)}

rtr := chi.NewRouter()
rtr.Post("/", webhooksCtrl.ServeHTTP)
rtr.Get("/logs/{uuid}", logCtrl.ServeHTTP)
rtr.Get("/health", healthCtrl.ServeHTTP)

return rtr, nil
}
Expand All @@ -48,15 +56,11 @@ func createCommonServices() (logstore.Service, github.Service, error) {
return logstoreService, githubService, nil
}

func createRunner(logstoreService logstore.Service, githubService github.Service) (runner.Runner, error) {
func createRunner(logstoreService logstore.Service, githubService github.Service, dockerClient moby.Client) (runner.Runner, error) {
gitClient, err := git.New()
if err != nil {
return nil, errors.WithStack(err)
}
dockerClient, err := docker.New()
if err != nil {
return nil, errors.WithStack(err)
}

dockerRunner := &runner.DockerRunner{
BaseWorkDir: application.Config.Server.WorkDir,
Expand Down