Skip to content

Commit

Permalink
Refactor platform-specific code
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-stephen committed Dec 21, 2024
1 parent c324edc commit 3675af7
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 324 deletions.
4 changes: 3 additions & 1 deletion .goreleaser/dockers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ dockers:
extra_files:
- ./target/bin/linux/arm64/orchestrator

# Windows images
# Windows Images: Note that Windows containers require a container OS and have nuanced version compatibility
# (see https://learn.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility).
# Therefore, we target various versions for the base image. Currently, we provide images for Server 2019, 2022, and 2025.
- id: init-windows-server-2019
image_templates: ["circleci/runner-init:agent-windows-server-2019{{.Env.IMAGE_TAG_SUFFIX}}"]
dockerfile: ./docker/windows.Dockerfile
Expand Down
9 changes: 9 additions & 0 deletions init/binaries_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !windows

package init

const (
binOrchestrator = "orchestrator"
binCircleciAgent = "circleci-agent"
binCircleci = "circleci"
)
7 changes: 7 additions & 0 deletions init/binaries_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package init

const (
binOrchestrator = "orchestrator.exe"
binCircleciAgent = "circleci-agent.exe"
binCircleci = "circleci.exe"
)
27 changes: 15 additions & 12 deletions init/init_unix.go → init/init.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
//go:build !windows

package init

import (
"errors"
"io"
"os"
"path/filepath"
"runtime"
)

const (
binOrchestrator = "orchestrator"
binCircleciAgent = "circleci-agent"
binCircleci = "circleci"
)

// Run function performs the copying of specific files and symlink creation
// Run function performs the copying of the orchestrator and task-agent binaries
func Run(srcDir, destDir string) error {
// Copy the orchestrator binary
orchestratorSrc := filepath.Join(srcDir, binOrchestrator)
Expand All @@ -30,9 +23,19 @@ func Run(srcDir, destDir string) error {
if err := copyFile(agentSrc, agentDest); err != nil {
return err
}
// Create symbolic link from "circleci-agent" to "circleci"
if err := os.Symlink(agentDest, filepath.Join(destDir, binCircleci)); err != nil {
return err

circleciDest := filepath.Join(destDir, binCircleci)
if runtime.GOOS != "windows" {
// Create symbolic link from "circleci-agent" to "circleci"
if err := os.Symlink(agentDest, circleciDest); err != nil {
return err
}
} else {
// We copy the binary instead of creating a symlink to `circleci` as we do on Linux,
// since we do not have the necessary privileges to create symlinks to the shared volume on Windows.
if err := copyFile(agentSrc, circleciDest); err != nil {
return err
}
}

return nil
Expand Down
68 changes: 0 additions & 68 deletions init/init_windows.go

This file was deleted.

132 changes: 132 additions & 0 deletions task/cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package cmd

import (
"context"
"fmt"
"io"
"os"
"os/exec"
"strings"
"sync/atomic"
"syscall"
)

type Command struct {
cmd *exec.Cmd
stderrSaver *prefixSuffixSaver
isStarted atomic.Bool
isCompleted atomic.Bool
forwardSignals bool
waitCh chan error
}

func New(ctx context.Context, cmd []string, forwardSignals bool, user string, env ...string) Command {
s := &prefixSuffixSaver{N: 160}
return Command{
cmd: newCmd(ctx, cmd, user, s, env...),
stderrSaver: s,
forwardSignals: forwardSignals,
waitCh: make(chan error, 1),
}
}

func (c *Command) Start() error {
cmd := c.cmd

if err := cmd.Start(); err != nil {
return err
}

if cmd.Process == nil {
return fmt.Errorf("no underlying process")
}

go func() {
c.waitCh <- c.wait()
}()

if c.forwardSignals {
forwardSignals(cmd)
}

c.isStarted.Store(true)

return nil
}

func (c *Command) StartWithStdin(b []byte) error {
w, err := c.cmd.StdinPipe()

if err != nil {
return fmt.Errorf("unexpected error on stdin pipe: %w", err)
}
defer func() {
_ = w.Close()
}()

_, err = w.Write(b)
if err != nil {
return fmt.Errorf("failed to write to stdin pipe: %w", err)
}

return c.Start()
}

func (c *Command) Wait() error {
return <-c.waitCh
}

func (c *Command) wait() error {
cmd := c.cmd
defer func() {
_ = cmd.Cancel()

c.isCompleted.Store(cmd.ProcessState != nil)
}()

err := cmd.Wait()
if err != nil {
stderr := c.stderrSaver.Bytes()
if len(stderr) > 0 {
return fmt.Errorf("%w: %s", err, string(stderr))
}
}
return err
}

func (c *Command) IsRunning() (bool, error) {
if !c.isStarted.Load() {
return false, nil
}

return !c.isCompleted.Load(), nil
}

func newCmd(ctx context.Context, argv []string, user string, stderrSaver *prefixSuffixSaver, env ...string) *exec.Cmd {
//#nosec:G204 // this is intentionally setting up a command
cmd := exec.CommandContext(ctx, argv[0], argv[1:]...)

for _, env := range os.Environ() {
if strings.HasPrefix(env, "CIRCLECI_GOAT") {
// Prevent internal configuration from being injected in the command environment
continue
}
cmd.Env = append(cmd.Env, env)
}
if env != nil {
cmd.Env = append(cmd.Env, env...)
}

cmd.Stdout = os.Stdout
cmd.Stderr = io.MultiWriter(os.Stderr, stderrSaver)

cmd.SysProcAttr = &syscall.SysProcAttr{}

if user != "" {
switchUser(ctx, cmd, user)
}

additionalSetup(ctx, cmd)

return cmd
}
Loading

0 comments on commit 3675af7

Please sign in to comment.