Skip to content

Commit 0cac44a

Browse files
Refactor platform-specific code
1 parent c324edc commit 0cac44a

File tree

9 files changed

+192
-324
lines changed

9 files changed

+192
-324
lines changed

.goreleaser/dockers.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ dockers:
3131
extra_files:
3232
- ./target/bin/linux/arm64/orchestrator
3333

34-
# Windows images
34+
# Windows Images: Note that Windows containers require a container OS and have nuanced version compatibility
35+
# (see https://learn.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility).
36+
# Therefore, we target various versions for the base image. Currently, we provide images for Server 2019, 2022, and 2025.
3537
- id: init-windows-server-2019
3638
image_templates: ["circleci/runner-init:agent-windows-server-2019{{.Env.IMAGE_TAG_SUFFIX}}"]
3739
dockerfile: ./docker/windows.Dockerfile

init/binaries_unix.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !windows
2+
3+
package init
4+
5+
const (
6+
binOrchestrator = "orchestrator"
7+
binCircleciAgent = "circleci-agent"
8+
binCircleci = "circleci"
9+
)

init/binaries_windows.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package init
2+
3+
const (
4+
binOrchestrator = "orchestrator.exe"
5+
binCircleciAgent = "circleci-agent.exe"
6+
binCircleci = "circleci.exe"
7+
)

init/init_unix.go renamed to init/init.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
//go:build !windows
2-
31
package init
42

53
import (
64
"errors"
75
"io"
86
"os"
97
"path/filepath"
8+
"runtime"
109
)
1110

12-
const (
13-
binOrchestrator = "orchestrator"
14-
binCircleciAgent = "circleci-agent"
15-
binCircleci = "circleci"
16-
)
17-
18-
// Run function performs the copying of specific files and symlink creation
11+
// Run function performs the copying of the orchestrator and task-agent binaries
1912
func Run(srcDir, destDir string) error {
2013
// Copy the orchestrator binary
2114
orchestratorSrc := filepath.Join(srcDir, binOrchestrator)
@@ -30,9 +23,19 @@ func Run(srcDir, destDir string) error {
3023
if err := copyFile(agentSrc, agentDest); err != nil {
3124
return err
3225
}
33-
// Create symbolic link from "circleci-agent" to "circleci"
34-
if err := os.Symlink(agentDest, filepath.Join(destDir, binCircleci)); err != nil {
35-
return err
26+
27+
circleciDest := filepath.Join(destDir, binCircleci)
28+
if runtime.GOOS != "windows" {
29+
// Create symbolic link from "circleci-agent" to "circleci"
30+
if err := os.Symlink(agentDest, circleciDest); err != nil {
31+
return err
32+
}
33+
} else {
34+
// We copy the binary instead of creating a symlink to `circleci` as we do on Linux,
35+
// since we do not have the necessary privileges to create symlinks to the shared volume on Windows.
36+
if err := copyFile(agentSrc, circleciDest); err != nil {
37+
return err
38+
}
3639
}
3740

3841
return nil

init/init_windows.go

Lines changed: 0 additions & 68 deletions
This file was deleted.

task/cmd/cmd.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"os"
8+
"os/exec"
9+
"strings"
10+
"sync/atomic"
11+
"syscall"
12+
)
13+
14+
type Command struct {
15+
cmd *exec.Cmd
16+
stderrSaver *prefixSuffixSaver
17+
isStarted atomic.Bool
18+
isCompleted atomic.Bool
19+
forwardSignals bool
20+
waitCh chan error
21+
}
22+
23+
func New(ctx context.Context, cmd []string, forwardSignals bool, user string, env ...string) Command {
24+
s := &prefixSuffixSaver{N: 160}
25+
return Command{
26+
cmd: newCmd(ctx, cmd, user, s, env...),
27+
stderrSaver: s,
28+
forwardSignals: forwardSignals,
29+
waitCh: make(chan error, 1),
30+
}
31+
}
32+
33+
func (c *Command) Start() error {
34+
cmd := c.cmd
35+
36+
if err := cmd.Start(); err != nil {
37+
return err
38+
}
39+
40+
if cmd.Process == nil {
41+
return fmt.Errorf("no underlying process")
42+
}
43+
44+
go func() {
45+
c.waitCh <- c.wait()
46+
c.isCompleted.Store(cmd.ProcessState != nil)
47+
}()
48+
49+
if c.forwardSignals {
50+
forwardSignals(cmd)
51+
}
52+
53+
c.isStarted.Store(true)
54+
55+
return nil
56+
}
57+
58+
func (c *Command) StartWithStdin(b []byte) error {
59+
w, err := c.cmd.StdinPipe()
60+
61+
if err != nil {
62+
return fmt.Errorf("unexpected error on stdin pipe: %w", err)
63+
}
64+
defer func() {
65+
_ = w.Close()
66+
}()
67+
68+
_, err = w.Write(b)
69+
if err != nil {
70+
return fmt.Errorf("failed to write to stdin pipe: %w", err)
71+
}
72+
73+
return c.Start()
74+
}
75+
76+
func (c *Command) Wait() error {
77+
return <-c.waitCh
78+
}
79+
80+
func (c *Command) wait() error {
81+
cmd := c.cmd
82+
defer func() {
83+
_ = cmd.Cancel()
84+
85+
c.isCompleted.Store(cmd.ProcessState != nil)
86+
}()
87+
88+
err := cmd.Wait()
89+
if err != nil {
90+
stderr := c.stderrSaver.Bytes()
91+
if len(stderr) > 0 {
92+
return fmt.Errorf("%w: %s", err, string(stderr))
93+
}
94+
}
95+
return err
96+
}
97+
98+
func (c *Command) IsRunning() (bool, error) {
99+
if !c.isStarted.Load() {
100+
return false, nil
101+
}
102+
103+
return !c.isCompleted.Load(), nil
104+
}
105+
106+
func newCmd(ctx context.Context, argv []string, user string, stderrSaver *prefixSuffixSaver, env ...string) *exec.Cmd {
107+
//#nosec:G204 // this is intentionally setting up a command
108+
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)
109+
110+
for _, env := range os.Environ() {
111+
if strings.HasPrefix(env, "CIRCLECI_GOAT") {
112+
// Prevent internal configuration from being injected in the command environment
113+
continue
114+
}
115+
cmd.Env = append(cmd.Env, env)
116+
}
117+
if env != nil {
118+
cmd.Env = append(cmd.Env, env...)
119+
}
120+
121+
cmd.Stdout = os.Stdout
122+
cmd.Stderr = io.MultiWriter(os.Stderr, stderrSaver)
123+
124+
cmd.SysProcAttr = &syscall.SysProcAttr{}
125+
126+
if user != "" {
127+
switchUser(ctx, cmd, user)
128+
}
129+
130+
additionalSetup(ctx, cmd)
131+
132+
return cmd
133+
}

0 commit comments

Comments
 (0)