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

RE: ARCHITECTURE #148

Merged
merged 135 commits into from
Jan 5, 2019
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
51aa5d0
Add new task runner
duck8823 Nov 27, 2018
240d65e
Reduce instance variables
duck8823 Nov 28, 2018
3fd0d1d
Change type name
duck8823 Nov 28, 2018
53db3bb
Cleanup config
duck8823 Nov 28, 2018
949dd8d
Separate helper methods
duck8823 Nov 28, 2018
c3ef14c
DockerRunner as domain service
duck8823 Nov 28, 2018
f1fe4f4
Implement core modules
duck8823 Nov 30, 2018
8a5f2d6
Add runner builder
duck8823 Dec 1, 2018
b8b9a4e
Add job model
duck8823 Dec 1, 2018
d04040c
reduce for loop
duck8823 Dec 1, 2018
cda5f2c
Replace to single function
duck8823 Dec 1, 2018
8f8a0c7
Add target model
duck8823 Dec 1, 2018
a1d4d4b
Move package
duck8823 Dec 1, 2018
56d9943
Change import
duck8823 Dec 1, 2018
b702f41
Rename struct
duck8823 Dec 1, 2018
fb910a1
Revert: commit b702f4106b5677c5692e34f56b113720c411e653
duck8823 Dec 1, 2018
e431df6
Add github push target
duck8823 Dec 2, 2018
a6f39be
Fix to be able to checkout past commit
duck8823 Dec 2, 2018
186a24c
Add initialize method
duck8823 Dec 2, 2018
9516ecf
Add nothing to do function and set as default
duck8823 Dec 2, 2018
c9e9a00
Change log replace method
duck8823 Dec 2, 2018
cf28ead
Add target github pull request
duck8823 Dec 2, 2018
d07860b
Fix godoc
duck8823 Dec 2, 2018
bfe1e8f
Change to use model
duck8823 Dec 2, 2018
e7d1d29
Add function "CreateCommitStatus"
duck8823 Dec 2, 2018
49591aa
Fix return type
duck8823 Dec 2, 2018
0c56a8b
Add executor builder
duck8823 Dec 2, 2018
6f3fb0b
Add context to application
duck8823 Dec 3, 2018
b4f9d96
Add new PushHandler
duck8823 Dec 3, 2018
94bf764
Add new IssueCommentHandler
duck8823 Dec 5, 2018
0afd932
Add health handler
duck8823 Dec 6, 2018
d511ed4
Add job handler and service
duck8823 Dec 6, 2018
08c764d
Rename files
duck8823 Dec 6, 2018
4e7a541
Error message
duck8823 Dec 6, 2018
06feb3e
Fix logic to get key of store
duck8823 Dec 6, 2018
a59f618
Fix job modules
duck8823 Dec 6, 2018
45a37f0
Add service functions
duck8823 Dec 7, 2018
ac676a3
Change interface
duck8823 Dec 7, 2018
9ade4dc
Using new api
duck8823 Dec 7, 2018
1f81e81
Remove old apis
duck8823 Dec 7, 2018
318322d
Change repository api
duck8823 Dec 7, 2018
5a8eec3
Change type of build job
duck8823 Dec 7, 2018
d1a5c57
Run asynchronously
duck8823 Dec 8, 2018
1de7127
Add application initialize
duck8823 Dec 9, 2018
d15fc04
Fix print message
duck8823 Dec 9, 2018
4bc5090
Add duci struct
duck8823 Dec 9, 2018
3a954a2
Change error type to variable
duck8823 Dec 9, 2018
7f1efa0
Add comment to public functions and variables
duck8823 Dec 9, 2018
17bb89b
s/ToString/String/
duck8823 Dec 9, 2018
529828b
Remove To prefix
duck8823 Dec 9, 2018
90b0d04
Fix success
duck8823 Dec 10, 2018
4a47878
Fix exit code for success
duck8823 Dec 10, 2018
e9dc806
Change log logic
duck8823 Dec 10, 2018
318d957
Add test cases for git
duck8823 Dec 13, 2018
255afa4
Add test cases for github
duck8823 Dec 15, 2018
815ee77
mockgen git and github
duck8823 Dec 15, 2018
6eb8e22
[wip] for testable
duck8823 Dec 16, 2018
825f751
Add singleton container
duck8823 Dec 16, 2018
4288e8c
Add override function
duck8823 Dec 16, 2018
03982ce
Remove instance variable form git package
duck8823 Dec 16, 2018
af0ec67
Remove NewGithubPush
duck8823 Dec 17, 2018
4289921
Remove GitHubPR struct and rename GitHubPush to GitHub
duck8823 Dec 17, 2018
e5a3b67
Add test for models
duck8823 Dec 17, 2018
b6253bd
Remove package variable in job
duck8823 Dec 17, 2018
5f35098
Add test case
duck8823 Dec 17, 2018
77de961
Add test case to application context
duck8823 Dec 18, 2018
76e8f44
Add initialize test
duck8823 Dec 18, 2018
fce32bc
Add line brake
duck8823 Dec 18, 2018
9147ff0
Set instance to container as interface
duck8823 Dec 18, 2018
a513358
Separate files implementation and interface
duck8823 Dec 22, 2018
5a4bc82
Add test for job service
duck8823 Dec 22, 2018
80266bd
Generate mock_service
duck8823 Dec 22, 2018
9ac29e1
Add test for job service
duck8823 Dec 23, 2018
10420ca
Add datasource test
duck8823 Dec 24, 2018
5c3475c
Fix test case
duck8823 Dec 26, 2018
d9dade0
Add test for runner builder
duck8823 Dec 30, 2018
edcbc7b
Add generated mock
duck8823 Dec 31, 2018
a34d88f
Add test cases for runner
duck8823 Dec 31, 2018
3197cf2
Change to private field
duck8823 Dec 31, 2018
08d502d
Add pattern
duck8823 Dec 31, 2018
4fae994
Separate interface and implementation
duck8823 Jan 1, 2019
9111763
Add docker mock
duck8823 Jan 1, 2019
b72c814
Add runner test
duck8823 Jan 1, 2019
ca63afa
Fix allow unexported structure
duck8823 Jan 1, 2019
cd49606
Add test pattern
duck8823 Jan 1, 2019
e43363b
Add test case
duck8823 Jan 1, 2019
820eb25
Remove unused interface function
duck8823 Jan 1, 2019
41032e3
Use generated mock
duck8823 Jan 1, 2019
766d513
Move package
duck8823 Jan 1, 2019
0f07c07
Add handler test
duck8823 Jan 2, 2019
a5b8c81
Add assertion
duck8823 Jan 3, 2019
f34955b
Change to execute job in goroutine
duck8823 Jan 3, 2019
2a69e4a
Add test pattern
duck8823 Jan 3, 2019
b40a82c
Fix missing call func goroutine
duck8823 Jan 3, 2019
c60d366
Update gomock
duck8823 Jan 3, 2019
d4acd46
Add test cases
duck8823 Jan 3, 2019
09b5d70
Using gomock instead of gock
duck8823 Jan 3, 2019
aac1e29
Add test cases
duck8823 Jan 3, 2019
2cab6e3
Add router test
duck8823 Jan 4, 2019
3779da0
Fix panic when failed cast
duck8823 Jan 4, 2019
92f668d
Fix logic when error
duck8823 Jan 4, 2019
5144e3f
Add test for duci
duck8823 Jan 4, 2019
b1ca078
Return error instead of panic
duck8823 Jan 4, 2019
89f74ac
Add executor test
duck8823 Jan 4, 2019
8b8ea5c
Remove return value from job.Cleanup
duck8823 Jan 4, 2019
73e443d
Remove unused struct
duck8823 Jan 4, 2019
3006a29
Change transform func
duck8823 Jan 4, 2019
75cb4af
Fix unhandled error
duck8823 Jan 4, 2019
5d7434f
Add assertion
duck8823 Jan 4, 2019
ff9b4db
Remove redundant return
duck8823 Jan 4, 2019
28bb20b
Use raw literal for regex
duck8823 Jan 4, 2019
9c81f9f
For more stable
duck8823 Jan 4, 2019
7d5f184
Separate test case
duck8823 Jan 4, 2019
7a12a0c
Change context key to use pointer
duck8823 Jan 4, 2019
3c01661
Change return type to exported type
duck8823 Jan 4, 2019
113190b
Fix error messages
duck8823 Jan 4, 2019
d5f3ef6
Change to exported type
duck8823 Jan 4, 2019
7a688d0
Change package name
duck8823 Jan 4, 2019
b713db0
Remove dot import
duck8823 Jan 4, 2019
97156f2
Fix comments
duck8823 Jan 4, 2019
cd7a947
Fix error type name
duck8823 Jan 4, 2019
5a5c46d
Add comments
duck8823 Jan 4, 2019
e8364a9
Rename types
duck8823 Jan 4, 2019
c1ee4ac
Change to exported types
duck8823 Jan 4, 2019
1779a86
Remove dot import
duck8823 Jan 4, 2019
c0ef51e
Add comment
duck8823 Jan 4, 2019
55929e6
Change return type to exported it
duck8823 Jan 4, 2019
bcad5bf
Rename error variable
duck8823 Jan 4, 2019
8a809ce
Fix git client using ssh
duck8823 Jan 4, 2019
134c05d
Fix target url of commit status
duck8823 Jan 5, 2019
8580b25
Fix json key of log line
duck8823 Jan 5, 2019
2b0284b
Fix ref when push
duck8823 Jan 5, 2019
172fe63
Add debug print in error
duck8823 Jan 5, 2019
6c4af7a
Fix sha on push
duck8823 Jan 5, 2019
c116414
Delete http-requests-log.http
duck8823 Jan 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions application/service/executor/executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package executor

import (
"context"
"github.com/duck8823/duci/application"
"github.com/duck8823/duci/application/semaphore"
"github.com/duck8823/duci/domain/model/docker"
"github.com/duck8823/duci/domain/model/job"
"github.com/duck8823/duci/domain/service/runner"
"github.com/labstack/gommon/random"
"github.com/pkg/errors"
)

type JobExecutor struct {
runner.DockerRunner
StartFunc func(context.Context)
EndFunc func(context.Context, error)
}

// Execute job
func (r *JobExecutor) Execute(ctx context.Context, target job.Target, cmd ...string) error {
r.StartFunc(ctx)

workDir, cleanup, err := target.Prepare()
if err != nil {
return errors.WithStack(err)
}
defer cleanup()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error return value of cleanup is not checked (from errcheck)


errs := make(chan error, 1)

timeout, cancel := context.WithTimeout(ctx, application.Config.Timeout())
defer cancel()

go func() {
semaphore.Acquire()
errs <- r.DockerRunner.Run(timeout, workDir, docker.Tag(random.String(16, random.Lowercase)), cmd)
semaphore.Release()
}()

select {
case <-timeout.Done():
r.EndFunc(ctx, timeout.Err())
return timeout.Err()
case err := <-errs:
r.EndFunc(ctx, err)
return err
}
}
62 changes: 62 additions & 0 deletions application/service/runner/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package runner

import (
"bytes"
"github.com/duck8823/duci/application/service/docker"
"github.com/duck8823/duci/infrastructure/archive/tar"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"path/filepath"
)

// createTarball creates a tar archive
func createTarball(workDir string) (*os.File, error) {
tarFilePath := filepath.Join(workDir, "duci.tar")
writeFile, err := os.OpenFile(tarFilePath, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return nil, errors.WithStack(err)
}
defer writeFile.Close()

if err := tar.Create(workDir, writeFile); err != nil {
return nil, errors.WithStack(err)
}

readFile, _ := os.Open(tarFilePath)
return readFile, nil
}

// dockerfilePath returns a path to dockerfile for duci using
func dockerfilePath(workDir string) docker.Dockerfile {
dockerfile := "./Dockerfile"
if exists(filepath.Join(workDir, ".duci/Dockerfile")) {
dockerfile = ".duci/Dockerfile"
}
return docker.Dockerfile(dockerfile)
}

// exists indicates whether the file exists
func exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}

// runtimeOptions parses a config.yml and returns a docker runtime options
func runtimeOptions(workDir string) (docker.RuntimeOptions, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runtimeOptions is unused (from deadcode)

var opts docker.RuntimeOptions

if !exists(filepath.Join(workDir, ".duci/config.yml")) {
return opts, nil
}
content, err := ioutil.ReadFile(filepath.Join(workDir, ".duci/config.yml"))
if err != nil {
return opts, errors.WithStack(err)
}
content = []byte(os.ExpandEnv(string(content)))
if err := yaml.NewDecoder(bytes.NewReader(content)).Decode(&opts); err != nil {
return opts, errors.WithStack(err)
}
return opts, nil
}
30 changes: 0 additions & 30 deletions application/service/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/duck8823/duci/application/service/github"
"github.com/duck8823/duci/application/service/logstore"
"github.com/duck8823/duci/data/model"
"github.com/duck8823/duci/infrastructure/archive/tar"
"github.com/duck8823/duci/infrastructure/logger"
"github.com/labstack/gommon/random"
"github.com/pkg/errors"
Expand Down Expand Up @@ -116,30 +115,6 @@ func (r *DockerRunner) dockerBuild(ctx context.Context, dir string, repo github.
return nil
}

func createTarball(workDir string) (*os.File, error) {
tarFilePath := filepath.Join(workDir, "duci.tar")
writeFile, err := os.OpenFile(tarFilePath, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return nil, errors.WithStack(err)
}
defer writeFile.Close()

if err := tar.Create(workDir, writeFile); err != nil {
return nil, errors.WithStack(err)
}

readFile, _ := os.Open(tarFilePath)
return readFile, nil
}

func dockerfilePath(workDir string) docker.Dockerfile {
dockerfile := "./Dockerfile"
if exists(filepath.Join(workDir, ".duci/Dockerfile")) {
dockerfile = ".duci/Dockerfile"
}
return docker.Dockerfile(dockerfile)
}

func (r *DockerRunner) dockerRun(ctx context.Context, dir string, repo github.Repository, cmd docker.Command) (docker.ContainerID, error) {
opts, err := runtimeOpts(dir)
if err != nil {
Expand Down Expand Up @@ -211,8 +186,3 @@ func (r *DockerRunner) finish(ctx context.Context, src *github.TargetSource, err
}
r.LogStore.Finish(ctx.UUID())
}

func exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
3 changes: 3 additions & 0 deletions application/service/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,9 @@ func TestRunnerImpl_Run_NonNormal(t *testing.T) {

// and
application.Config.Job.Timeout = 1
defer func() {
application.Config.Job.Timeout = 600
}()

mockDocker := mock_docker.NewMockService(ctrl)
mockDocker.EXPECT().
Expand Down
92 changes: 92 additions & 0 deletions domain/model/docker/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package docker

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"github.com/duck8823/duci/domain/model/job"
"github.com/pkg/errors"
"io"
"time"
)

var now = time.Now

type buildLogger struct {
reader *bufio.Reader
}

// NewBuildLog return a instance of Log.
func NewBuildLog(r io.Reader) *buildLogger {
return &buildLogger{bufio.NewReader(r)}
}

// ReadLine returns LogLine.
func (l *buildLogger) ReadLine() (*job.LogLine, error) {
for {
line, _, readErr := l.reader.ReadLine()
msg := extractMessage(line)
if readErr == io.EOF {
return &job.LogLine{Timestamp: now(), Message: msg}, readErr
}
if readErr != nil {
return nil, errors.WithStack(readErr)
}

if len(msg) == 0 {
continue
}

return &job.LogLine{Timestamp: now(), Message: msg}, readErr
}
}

type runLogger struct {
reader *bufio.Reader
}

// NewRunLog returns a instance of Log
func NewRunLog(r io.Reader) *runLogger {
return &runLogger{bufio.NewReader(r)}
}

// ReadLine returns LogLine.
func (l *runLogger) ReadLine() (*job.LogLine, error) {
for {
line, _, readErr := l.reader.ReadLine()
if readErr != nil && readErr != io.EOF {
return nil, errors.WithStack(readErr)
}

messages, err := trimPrefix(line)
if err != nil {
return nil, errors.WithStack(err)
}

// prevent to CR
progress := bytes.Split(messages, []byte{'\r'})
return &job.LogLine{Timestamp: now(), Message: string(progress[0])}, readErr

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the surrounding loop is unconditionally terminated (from megacheck)

}
}

func extractMessage(line []byte) string {
s := &struct {
Stream string `json:"stream"`
}{}
json.NewDecoder(bytes.NewReader(line)).Decode(s)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error return value of (*encoding/json.Decoder).Decode is not checked (from errcheck)

return s.Stream
}

func trimPrefix(line []byte) ([]byte, error) {
if len(line) < 8 {
return []byte{}, nil
}

// detect logstore prefix
// see https://godoc.org/github.com/docker/docker/client#Client.ContainerLogs
if !((line[0] == 1 || line[0] == 2) && (line[1] == 0 && line[2] == 0 && line[3] == 0)) {
return nil, fmt.Errorf("invalid logstore prefix: %+v", line[:7])
}
return line[8:], nil
}
37 changes: 37 additions & 0 deletions domain/model/docker/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package docker

import (
"fmt"
"strings"
)

// RuntimeOptions is a docker options.
type RuntimeOptions struct {
Environments Environments
Volumes Volumes
}

// Environments represents a docker `-e` option.
type Environments map[string]interface{}

// ToArray returns string array of environments
func (e Environments) ToArray() []string {
var a []string
for key, val := range e {
a = append(a, fmt.Sprintf("%s=%v", key, val))
}
return a
}

// Volumes represents a docker `-v` option.
type Volumes []string

// ToMap returns map of volumes.
func (v Volumes) ToMap() map[string]struct{} {
m := make(map[string]struct{})
for _, volume := range v {
key := strings.Split(volume, ":")[0]
m[key] = struct{}{}
}
return m
}
41 changes: 41 additions & 0 deletions domain/model/docker/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package docker

// Tag describes a docker tag
type Tag string

// ToString return string value
func (t Tag) ToString() string {
return string(t)
}

// Command describes a docker CMD
type Command []string

// ToSlice returns slice values
func (c Command) ToSlice() []string {
return []string(c)
}

// Dockerfile represents a path to dockerfile
type Dockerfile string

// ToString returns string value
func (d Dockerfile) ToString() string {
return string(d)
}

// ContainerID describes a container id of docker
type ContainerID string

// ToString returns string value
func (c ContainerID) ToString() string {
return string(c)
}

// ExitCode describes a exit code
type ExitCode int64

// IsFailure returns whether failure code or not
func (c ExitCode) IsFailure() bool {
return c != 1
}
50 changes: 50 additions & 0 deletions domain/model/job/job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package job

import (
"bytes"
"encoding/json"
"github.com/google/uuid"
"github.com/pkg/errors"
)

// Job represents a task
type Job struct {
Finished bool `json:"finished"`
Stream []LogLine `json:"stream"`
}

// AppendLog append log line to stream
func (j *Job) AppendLog(line LogLine) {
j.Stream = append(j.Stream, line)
}

// Finish set true to Finished
func (j *Job) Finish() {
j.Finished = true
}

// ToBytes returns marshal byte slice
func (j *Job) ToBytes() ([]byte, error) {
data, err := json.Marshal(j)
if err != nil {
return nil, errors.WithStack(err)
}
return data, nil
}

// NewJob returns unmarshal Job instance
func NewJob(data []byte) (*Job, error) {
job := &Job{}
if err := json.NewDecoder(bytes.NewReader(data)).Decode(job); err != nil {
return nil, errors.WithStack(err)
}
return job, nil
}

// ID is the identifier of job
type ID uuid.UUID

// ToSlice returns slice value
func (i ID) ToSlice() []byte {
return []byte(i[:])
}
Loading