diff --git a/README.md b/README.md index 33fe7ac..0615a7e 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,10 @@ go test ./... - Create small pieces with extracting codes from runner struct and write unit test: - Image Manager ✅[Issue#3](https://github.com/muhammedikinci/pin/issues/3) - Container Manager Not Completed Yet [Issue#4](https://github.com/muhammedikinci/pin/issues/4) - - Shell Commander Not Completed Yet [Issue#5](https://github.com/muhammedikinci/pin/issues/5) + - Shell Commander ✅[Issue#5](https://github.com/muhammedikinci/pin/issues/5) - Parser - Runner +- Add port expose support # Contact diff --git a/pkg/container_manager/container_manager.go b/pkg/container_manager/container_manager.go index a5a77a4..4a0ca12 100644 --- a/pkg/container_manager/container_manager.go +++ b/pkg/container_manager/container_manager.go @@ -17,21 +17,21 @@ import ( "github.com/muhammedikinci/pin/pkg/interfaces" ) -type ContainerManager struct { +type containerManager struct { ctx context.Context cli interfaces.Client log interfaces.Log } -func NewContainerManager(ctx context.Context, cli interfaces.Client, log interfaces.Log) ContainerManager { - return ContainerManager{ +func NewContainerManager(ctx context.Context, cli interfaces.Client, log interfaces.Log) containerManager { + return containerManager{ ctx: ctx, cli: cli, log: log, } } -func (cm ContainerManager) StartContainer(jobName string, image string) (container.ContainerCreateCreatedBody, error) { +func (cm containerManager) StartContainer(jobName string, image string) (container.ContainerCreateCreatedBody, error) { color.Set(color.FgGreen) cm.log.Println("Start creating container") color.Unset() @@ -50,7 +50,7 @@ func (cm ContainerManager) StartContainer(jobName string, image string) (contain return resp, nil } -func (cm ContainerManager) StopContainer(containerID string) error { +func (cm containerManager) StopContainer(containerID string) error { color.Set(color.FgBlue) cm.log.Println("Container stopping") @@ -64,7 +64,7 @@ func (cm ContainerManager) StopContainer(containerID string) error { return nil } -func (cm ContainerManager) RemoveContainer(containerID string) error { +func (cm containerManager) RemoveContainer(containerID string) error { color.Set(color.FgBlue) cm.log.Println("Container removing") @@ -78,7 +78,7 @@ func (cm ContainerManager) RemoveContainer(containerID string) error { return nil } -func (cm ContainerManager) CopyToContainer(containerID, workDir string) error { +func (cm containerManager) CopyToContainer(containerID, workDir string) error { var buf bytes.Buffer tw := tar.NewWriter(&buf) @@ -86,53 +86,56 @@ func (cm ContainerManager) CopyToContainer(containerID, workDir string) error { currentPath, _ := os.Getwd() - // TODO: add dirs, directories does not extract from docker api err := filepath.Walk(currentPath, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } + return cm.appender(path, info, err, currentPath, tw) + }) - if !info.Mode().IsRegular() { - return nil - } + if err != nil { + return err + } - header, err := tar.FileInfoHeader(info, info.Name()) - if err != nil { - return err - } + err = cm.cli.CopyToContainer(cm.ctx, containerID, workDir, &buf, types.CopyToContainerOptions{}) - header.Name = strings.TrimPrefix(strings.Replace(path, currentPath, "", -1), string(filepath.Separator)) - header.Name = strings.ReplaceAll(header.Name, "\\", "/") + if err != nil { + return err + } - if header.Name[0] == '.' { - return nil - } + return nil +} - if err := tw.WriteHeader(header); err != nil { - return err - } +func (cm containerManager) appender(path string, info os.FileInfo, err error, currentPath string, tw *tar.Writer) error { + if err != nil { + return err + } - f, err := os.Open(path) - if err != nil { - return err - } + if !info.Mode().IsRegular() { + return nil + } - defer f.Close() + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return err + } - if _, err := io.Copy(tw, f); err != nil { - return err - } + header.Name = strings.TrimPrefix(strings.Replace(path, currentPath, "", -1), string(filepath.Separator)) + header.Name = strings.ReplaceAll(header.Name, "\\", "/") + if header.Name[0] == '.' { return nil - }) + } + + if err := tw.WriteHeader(header); err != nil { + return err + } + f, err := os.Open(path) if err != nil { return err } - err = cm.cli.CopyToContainer(cm.ctx, containerID, workDir, &buf, types.CopyToContainerOptions{}) + defer f.Close() - if err != nil { + if _, err := io.Copy(tw, f); err != nil { return err } diff --git a/pkg/container_manager/container_manager_test.go b/pkg/container_manager/container_manager_test.go index 43f8721..a39f102 100644 --- a/pkg/container_manager/container_manager_test.go +++ b/pkg/container_manager/container_manager_test.go @@ -1 +1,200 @@ package container_manager + +import ( + "context" + "errors" + "testing" + + "github.com/docker/docker/api/types/container" + "github.com/golang/mock/gomock" + "github.com/muhammedikinci/pin/pkg/mocks" + "github.com/stretchr/testify/assert" +) + +func TestWhenContainerCreateReturnErrorStartContainerMustReturnSameError(t *testing.T) { + ctrl := gomock.NewController(t) + + defer ctrl.Finish() + + mockCli := mocks.NewMockClient(ctrl) + mockLog := mocks.NewMockLog(ctrl) + + merror := errors.New("test") + + mockLog. + EXPECT(). + Println("Start creating container") + + mockCli. + EXPECT(). + ContainerCreate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(container.ContainerCreateCreatedBody{}, merror) + + cm := containerManager{ + ctx: context.Background(), + cli: mockCli, + log: mockLog, + } + + resp, err := cm.StartContainer("", "") + + assert.Equal(t, resp, container.ContainerCreateCreatedBody{}) + assert.Equal(t, err, merror) +} + +func TestWhenContainerCreateReturnResponseStartContainerMustSameResponseWithNilError(t *testing.T) { + ctrl := gomock.NewController(t) + + defer ctrl.Finish() + + mockCli := mocks.NewMockClient(ctrl) + mockLog := mocks.NewMockLog(ctrl) + + mres := container.ContainerCreateCreatedBody{ + ID: "test", + } + + mockLog. + EXPECT(). + Println("Start creating container") + + mockCli. + EXPECT(). + ContainerCreate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(mres, nil) + + cm := containerManager{ + ctx: context.Background(), + cli: mockCli, + log: mockLog, + } + + resp, err := cm.StartContainer("", "") + + assert.Equal(t, resp.ID, mres.ID) + assert.Equal(t, err, nil) +} + +func TestWhenContainerStopReturnErrorStopContainerMustReturnSameError(t *testing.T) { + ctrl := gomock.NewController(t) + + defer ctrl.Finish() + + mockCli := mocks.NewMockClient(ctrl) + mockLog := mocks.NewMockLog(ctrl) + + merror := errors.New("test") + + mockLog. + EXPECT(). + Println("Container stopping") + + mockCli. + EXPECT(). + ContainerStop(gomock.Any(), gomock.Any(), gomock.Any()). + Return(merror) + + cm := containerManager{ + ctx: context.Background(), + cli: mockCli, + log: mockLog, + } + + err := cm.StopContainer("") + + assert.Equal(t, err, merror) +} + +func TestWhenContainerStopReturnNilStopContainerMustReturnNil(t *testing.T) { + ctrl := gomock.NewController(t) + + defer ctrl.Finish() + + mockCli := mocks.NewMockClient(ctrl) + mockLog := mocks.NewMockLog(ctrl) + + mockLog. + EXPECT(). + Println("Container stopping") + + mockLog. + EXPECT(). + Println("Container stopped") + + mockCli. + EXPECT(). + ContainerStop(gomock.Any(), gomock.Any(), gomock.Any()). + Return(nil) + + cm := containerManager{ + ctx: context.Background(), + cli: mockCli, + log: mockLog, + } + + err := cm.StopContainer("") + + assert.Equal(t, err, nil) +} + +func TestWhenRemoveContainerReturnErrorStopContainerMustReturnSameError(t *testing.T) { + ctrl := gomock.NewController(t) + + defer ctrl.Finish() + + mockCli := mocks.NewMockClient(ctrl) + mockLog := mocks.NewMockLog(ctrl) + + merror := errors.New("test") + + mockLog. + EXPECT(). + Println("Container removing") + + mockCli. + EXPECT(). + ContainerRemove(gomock.Any(), gomock.Any(), gomock.Any()). + Return(merror) + + cm := containerManager{ + ctx: context.Background(), + cli: mockCli, + log: mockLog, + } + + err := cm.RemoveContainer("") + + assert.Equal(t, err, merror) +} + +func TestWhenContainerRemoveReturnNilRemoveContainerMustReturnNil(t *testing.T) { + ctrl := gomock.NewController(t) + + defer ctrl.Finish() + + mockCli := mocks.NewMockClient(ctrl) + mockLog := mocks.NewMockLog(ctrl) + + mockLog. + EXPECT(). + Println("Container removing") + + mockLog. + EXPECT(). + Println("Container removed") + + mockCli. + EXPECT(). + ContainerRemove(gomock.Any(), gomock.Any(), gomock.Any()). + Return(nil) + + cm := containerManager{ + ctx: context.Background(), + cli: mockCli, + log: mockLog, + } + + err := cm.RemoveContainer("") + + assert.Equal(t, err, nil) +} diff --git a/pkg/runner.go b/pkg/runner.go index 355f159..934112c 100644 --- a/pkg/runner.go +++ b/pkg/runner.go @@ -128,13 +128,17 @@ func (r *Runner) commandScriptExecutor() error { return err } - if err := r.commandRunner("chmod +x /home/shell_command.sh", ""); err != nil { + if err := r.internalExec("chmod +x /home/shell_command.sh"); err != nil { return err } if err := r.commandRunner("sh /home/shell_command.sh", cmd); err != nil { return err } + + if err := r.internalExec("rm /home/shell_command.sh"); err != nil { + return err + } } return nil @@ -217,3 +221,32 @@ func (r *Runner) commandRunner(command string, name string) error { return nil } + +func (r Runner) internalExec(command string) error { + args := strings.Split(command, " ") + + exec, err := r.cli.ContainerExecCreate(r.ctx, r.container.ID, types.ExecConfig{ + AttachStdin: true, + AttachStdout: true, + Cmd: args, + WorkingDir: r.workDir, + }) + + if err != nil { + return err + } + + res, err := r.cli.ContainerExecAttach(r.ctx, exec.ID, types.ExecStartCheck{Tty: true}) + if err != nil { + return err + } + + io.Copy(os.Stdout, res.Reader) + + _, err = r.cli.ContainerExecInspect(r.ctx, exec.ID) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/shell_commander/shell_commander.go b/pkg/shell_commander/shell_commander.go index 5e0f854..5fdfbc4 100644 --- a/pkg/shell_commander/shell_commander.go +++ b/pkg/shell_commander/shell_commander.go @@ -15,6 +15,10 @@ func NewShellCommander() ShellCommander { func (sc ShellCommander) PrepareShellCommands(soloExecution bool, scripts []string) []string { cmds := []string{} + if len(scripts) == 0 { + return cmds + } + if soloExecution { for _, cmd := range scripts { cmds = append(cmds, sc.wrapCommand(cmd)) diff --git a/pkg/shell_commander/shell_commander_test.go b/pkg/shell_commander/shell_commander_test.go index d5ae116..27cb1f4 100644 --- a/pkg/shell_commander/shell_commander_test.go +++ b/pkg/shell_commander/shell_commander_test.go @@ -1 +1,67 @@ package shell_commander + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type prepareShellCommandsTestCase struct { + soloExecution bool + scripts []string + result []string +} + +func TestPrepareShellCommands(t *testing.T) { + shellCommander := NewShellCommander() + + testCases := []prepareShellCommandsTestCase{ + { + soloExecution: true, + scripts: []string{ + "go test ./...", + }, + result: []string{ + shellCommander.wrapCommand("go test ./..."), + }, + }, + { + soloExecution: true, + scripts: []string{ + "go test ./...", + "npm install", + }, + result: []string{ + shellCommander.wrapCommand("go test ./..."), + shellCommander.wrapCommand("npm install"), + }, + }, + { + soloExecution: false, + scripts: []string{ + "go test ./...", + "npm install", + }, + result: []string{ + shellCommander.wrapCommand("go test ./...\nnpm install\n"), + }, + }, + { + soloExecution: true, + scripts: []string{}, + result: []string{}, + }, + { + soloExecution: false, + scripts: []string{}, + result: []string{}, + }, + } + + for _, testCase := range testCases { + res := shellCommander.PrepareShellCommands(testCase.soloExecution, testCase.scripts) + + assert.Equal(t, len(res), len(testCase.result)) + assert.Equal(t, res, testCase.result) + } +}