Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows support #144

Merged
merged 54 commits into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
7814fb1
Add Vagrantfile for windows box
lucaspin Feb 15, 2022
9ad9f46
Remove pty from windows build
lucaspin Feb 15, 2022
69b26d9
Get windows version
lucaspin Feb 16, 2022
f717646
Force noPTY on windows and use proper tmp directories
lucaspin Feb 16, 2022
53e117d
Handle environment variables and files
lucaspin Feb 16, 2022
66beed7
Fix failed command not returning proper exit code
lucaspin Feb 16, 2022
650bd88
Set SEMAPHORE_JOB_RESULT
lucaspin Feb 17, 2022
26d0f62
Address linter complaints
lucaspin Feb 17, 2022
cf4b6c9
Address static code checking complaints
lucaspin Feb 17, 2022
4807b2e
Keep track of cwd and environment when on noPTY
lucaspin Feb 17, 2022
1e55659
Add CALL for .bat or .cmd script
lucaspin Feb 18, 2022
3574da7
noPTY is only used when on windows
lucaspin Feb 21, 2022
931f8c6
Use os.O_CREATE as well
lucaspin Feb 21, 2022
4a9a489
Handle multiline script calls
lucaspin Feb 21, 2022
001f3c5
Execute shutdown hook
lucaspin Feb 21, 2022
1fd9b48
Handle job stopping
lucaspin Feb 21, 2022
62cc2a9
Read process output while it is being executed
lucaspin Feb 22, 2022
b0a32e9
Remove fail_fast temporarily
lucaspin Feb 22, 2022
72f0e84
Export environment variables for epilogues only once
lucaspin Feb 22, 2022
1583fa1
Fix E2Es
lucaspin Feb 22, 2022
039f5b9
Environment vars are iterated in order
lucaspin Feb 22, 2022
5af422d
Only use shellQuote() once
lucaspin Feb 22, 2022
58feb93
Use SEMAPHORE_AGENT_LOG_LEVEL=DEBUG in tests
lucaspin Feb 22, 2022
889d327
Revert tmpDirectory mistake
lucaspin Feb 22, 2022
e22f8d8
Fix E2Es
lucaspin Feb 22, 2022
099e986
Fix lint & code scanning
lucaspin Feb 22, 2022
119f6c4
Fix file injection E2Es
lucaspin Feb 22, 2022
7efa578
Fix missing newline
lucaspin Feb 22, 2022
c8d7a0f
Use -p 1
lucaspin Feb 23, 2022
845598d
Use os.O_TRUNC when injecting files
lucaspin Feb 23, 2022
57ee269
Run unit tests in windows
lucaspin Feb 23, 2022
95f06f1
Allow Powershell to be used
lucaspin Feb 23, 2022
7b06287
Add go and git binaries to PATH
lucaspin Feb 24, 2022
089b9c6
Merge branch 'master' into windows-support
lucaspin Feb 24, 2022
6fad3ad
Refactor things a bit
lucaspin Feb 24, 2022
c469ece
Revert unnecessary changes in docker_compose executor
lucaspin Feb 24, 2022
4918183
Fix issue
lucaspin Feb 24, 2022
6833352
Add comment
lucaspin Feb 24, 2022
3337b2e
Fix unit test
lucaspin Feb 27, 2022
32480d0
Remove temp.go
lucaspin Feb 27, 2022
482b106
No tmp file when injecting files in shell executor
lucaspin Feb 27, 2022
a2ac471
Little refactor
lucaspin Feb 27, 2022
17e6818
Fix E2E
lucaspin Feb 27, 2022
07a5cd1
Fix E2E
lucaspin Feb 27, 2022
4517546
Remove CMD.exe support
lucaspin Feb 27, 2022
fac63f8
Fix powershell args and shell.Close()
lucaspin Feb 27, 2022
5a59cbe
Use powershell on shutdown hook
lucaspin Feb 27, 2022
2804946
Revert test changes
lucaspin Mar 1, 2022
4d482d1
Add SelfHosted flag on JobOptions
lucaspin Mar 7, 2022
64d20ea
Fix tests
lucaspin Mar 7, 2022
d776fa5
Create job object on Shell, not Process
lucaspin Mar 8, 2022
945653a
Remove ssh_jump_points for now
lucaspin Mar 8, 2022
54d9e96
Merge branch 'master' into windows-support
lucaspin Mar 9, 2022
e08f2de
Use explicit flags
lucaspin Mar 9, 2022
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/build
/log
agent
.vagrant/
1 change: 0 additions & 1 deletion .semaphore/semaphore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ blocks:
- stty_restoration
- epilogue_on_pass
- epilogue_on_fail
- ssh_jump_points
- unicode
- unknown_command
- broken_unicode
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,18 @@ serve:
.PHONY: serve

test:
go test -short -v ./...
go test -p 1 -short -v ./...
.PHONY: test

build:
rm -rf build
env GOOS=linux GOARCH=386 go build -o build/agent main.go
.PHONY: build

build.windows:
rm -rf build
env GOOS=windows GOARCH=amd64 go build -o build/agent.exe main.go

e2e: build
ruby test/e2e/$(TEST).rb

Expand Down
8 changes: 8 additions & 0 deletions Vagrantfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# https://github.com/gusztavvargadr/packer/
config.vm.box = "gusztavvargadr/windows-server-2019-standard"
config.vm.provision "shell", path: "scripts/provision-windows-box.ps1"
end
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.9.0
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 // indirect
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1
golang.org/x/text v0.3.7 // indirect
gopkg.in/ini.v1 v1.64.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
Expand Down
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
Expand Down Expand Up @@ -236,8 +235,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/renderedtext/go-watchman v0.0.0-20210705111746-70108070d074 h1:6YLKgGc2PwDM3oEpAUavoiyjjpIH44HmjWSswwWTBa8=
github.com/renderedtext/go-watchman v0.0.0-20210705111746-70108070d074/go.mod h1:Z+qanDzSoUGCbcrTM7G6YCA9ST2KBdte7sCz+HQAp7I=
github.com/renderedtext/go-watchman v0.0.0-20210809121718-0632d0d12b0a h1:pRX9qebwT+TMdBojMspqDtU1RFLIbH5VzI8aI9yMiyE=
github.com/renderedtext/go-watchman v0.0.0-20210809121718-0632d0d12b0a/go.mod h1:Z+qanDzSoUGCbcrTM7G6YCA9ST2KBdte7sCz+HQAp7I=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
Expand Down Expand Up @@ -448,7 +445,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand All @@ -460,7 +456,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
Expand Down Expand Up @@ -657,7 +652,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c=
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.64.0 h1:Mj2zXEXcNb5joEiSA0zc3HZpTst/iyjNiR4CN8tDzOg=
gopkg.in/ini.v1 v1.64.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
Expand Down
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"

Expand Down Expand Up @@ -90,7 +91,7 @@ func getLogLevel() log.Level {
func getLogFilePath() string {
logFilePath := os.Getenv("SEMAPHORE_AGENT_LOG_FILE_PATH")
if logFilePath == "" {
return "/tmp/agent_log"
return filepath.Join(os.TempDir(), "agent_log")
}

parentDirectory := path.Dir(logFilePath)
Expand Down
24 changes: 24 additions & 0 deletions pkg/api/job_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io/fs"
"io/ioutil"
"path/filepath"
"strconv"
"strings"

yaml "gopkg.in/yaml.v3"
)
Expand Down Expand Up @@ -46,6 +49,27 @@ type File struct {
Mode string `json:"mode" yaml:"mode"`
}

func (f *File) NormalizePath(homeDir string) string {
if filepath.IsAbs(f.Path) {
return filepath.FromSlash(f.Path)
}

if strings.HasPrefix(f.Path, "~") {
return filepath.FromSlash(strings.ReplaceAll(f.Path, "~", homeDir))
}

return filepath.FromSlash(filepath.Join(homeDir, f.Path))
}

func (f *File) ParseMode() (fs.FileMode, error) {
fileMode, err := strconv.ParseUint(f.Mode, 8, 32)
if err != nil {
return 0, fmt.Errorf("bad file permission '%s'", f.Mode)
}

return fs.FileMode(fileMode), nil
}

type Callbacks struct {
Finished string `json:"finished" yaml:"finished"`
TeardownFinished string `json:"teardown_finished" yaml:"teardown_finished"`
Expand Down
5 changes: 4 additions & 1 deletion pkg/eventlogger/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package eventlogger
import (
"errors"
"fmt"
"os"
"path/filepath"

"github.com/semaphoreci/agent/pkg/api"
)
Expand All @@ -22,7 +24,8 @@ func CreateLogger(request *api.JobRequest) (*Logger, error) {
}

func Default() (*Logger, error) {
backend, err := NewFileBackend("/tmp/job_log.json")
path := filepath.Join(os.TempDir(), "job_log.json")
backend, err := NewFileBackend(path)
if err != nil {
return nil, err
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/eventlogger/httpbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"net/http"
"os"
"path/filepath"
"sync"
"time"

Expand All @@ -22,7 +24,8 @@ type HTTPBackend struct {
}

func NewHTTPBackend(url, token string) (*HTTPBackend, error) {
fileBackend, err := NewFileBackend("/tmp/job_log.json")
path := filepath.Join(os.TempDir(), "job_log.json")
fileBackend, err := NewFileBackend(path)
if err != nil {
return nil, err
}
Expand Down
8 changes: 6 additions & 2 deletions pkg/executors/authorized_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ func InjectEntriesToAuthorizedKeys(keys []api.PublicKey) error {
return nil
}

sshDirectory := filepath.Join(UserHomeDir(), ".ssh")
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}

err := os.MkdirAll(sshDirectory, os.ModePerm)
sshDirectory := filepath.Join(homeDir, ".ssh")
err = os.MkdirAll(sshDirectory, os.ModePerm)
if err != nil {
return err
}
Expand Down
51 changes: 23 additions & 28 deletions pkg/executors/docker_compose_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
"time"

pty "github.com/creack/pty"
watchman "github.com/renderedtext/go-watchman"
api "github.com/semaphoreci/agent/pkg/api"
"github.com/semaphoreci/agent/pkg/config"
Expand Down Expand Up @@ -56,6 +57,11 @@ func NewDockerComposeExecutor(request *api.JobRequest, logger *eventlogger.Logge
}

func (e *DockerComposeExecutor) Prepare() int {
if runtime.GOOS == "windows" {
log.Error("docker-compose executor is not supported in Windows")
return 1
}

err := os.MkdirAll(e.tmpDirectory, os.ModePerm)
if err != nil {
return 1
Expand Down Expand Up @@ -203,8 +209,8 @@ func (e *DockerComposeExecutor) startBashSession() int {
log.Debug("Starting stateful shell")

// #nosec
cmd := exec.Command(
"docker-compose",
executable := "docker-compose"
args := []string{
"--ansi",
"never",
"-f",
Expand All @@ -219,9 +225,9 @@ func (e *DockerComposeExecutor) startBashSession() int {
fmt.Sprintf("%s:%s:ro", e.tmpDirectory, e.tmpDirectory),
e.mainContainerName,
"bash",
)
}

shell, err := shell.NewShell(cmd, e.tmpDirectory)
shell, err := shell.NewShellFromExecAndArgs(executable, args, e.tmpDirectory)
if err != nil {
log.Errorf("Failed to start stateful shell err: %+v", err)

Expand Down Expand Up @@ -537,7 +543,7 @@ func (e *DockerComposeExecutor) pullDockerImages() int {
e.mainContainerName,
"true")

tty, err := pty.Start(cmd)
tty, err := shell.StartPTY(cmd)
if err != nil {
log.Errorf("Failed to initialize docker pull, err: %+v", err)
return 1
Expand Down Expand Up @@ -585,41 +591,30 @@ func (e *DockerComposeExecutor) ExportEnvVars(envVars []api.EnvVar, hostEnvVars
e.Logger.LogCommandFinished(directive, exitCode, commandStartedAt, commandFinishedAt)
}()

envFile := ""

for _, env := range envVars {
e.Logger.LogCommandOutput(fmt.Sprintf("Exporting %s\n", env.Name))

value, err := env.Decode()

if err != nil {
exitCode = 1
return exitCode
}

envFile += fmt.Sprintf("export %s=%s\n", env.Name, ShellQuote(string(value)))
}

for _, env := range hostEnvVars {
e.Logger.LogCommandOutput(fmt.Sprintf("Exporting %s\n", env.Name))
envFile += fmt.Sprintf("export %s=%s\n", env.Name, ShellQuote(env.Value))
environment, err := shell.CreateEnvironment(envVars, hostEnvVars)
if err != nil {
log.Errorf("Error creating environment: %v", err)
exitCode = 1
return exitCode
}

envPath := fmt.Sprintf("%s/.env", e.tmpDirectory)
err := ioutil.WriteFile(envPath, []byte(envFile), 0600)
envFileName := filepath.Join(e.tmpDirectory, ".env")
err = environment.ToFile(envFileName, func(name string) {
e.Logger.LogCommandOutput(fmt.Sprintf("Exporting %s\n", name))
})

if err != nil {
exitCode = 255
return exitCode
}

cmd := fmt.Sprintf("source %s", envPath)
cmd := fmt.Sprintf("source %s", envFileName)
exitCode = e.RunCommand(cmd, true, "")
if exitCode != 0 {
return exitCode
}

cmd = fmt.Sprintf("echo 'source %s' >> ~/.bash_profile", envPath)
cmd = fmt.Sprintf("echo 'source %s' >> ~/.bash_profile", envFileName)
exitCode = e.RunCommand(cmd, true, "")
if exitCode != 0 {
return exitCode
Expand Down
13 changes: 11 additions & 2 deletions pkg/executors/docker_compose_executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package executors
import (
"encoding/base64"
"os/exec"
"runtime"
"testing"
"time"

Expand Down Expand Up @@ -66,6 +67,10 @@ func startComposeExecutor() (*DockerComposeExecutor, *eventlogger.Logger, *event
}

func Test__DockerComposeExecutor(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("docker-compose executor is not yet support in Windows")
}

e, _, testLoggerBackend := startComposeExecutor()

e.RunCommand("echo 'here'", false, "")
Expand All @@ -78,14 +83,14 @@ func Test__DockerComposeExecutor(t *testing.T) {
e.RunCommand(multilineCmd, false, "")

envVars := []api.EnvVar{
api.EnvVar{Name: "A", Value: "Zm9vCg=="},
{Name: "A", Value: "Zm9vCg=="},
}

e.ExportEnvVars(envVars, []config.HostEnvVar{})
e.RunCommand("echo $A", false, "")

files := []api.File{
api.File{
{
Path: "/tmp/random-file.txt",
Content: "YWFhYmJiCgo=",
Mode: "0600",
Expand Down Expand Up @@ -139,6 +144,10 @@ func Test__DockerComposeExecutor(t *testing.T) {
}

func Test__DockerComposeExecutor__StopingRunningJob(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("docker-compose executor is not yet support in Windows")
}

e, _, testLoggerBackend := startComposeExecutor()

go func() {
Expand Down
Loading