Skip to content

Commit

Permalink
Respect CIRRUS_WORKING_DIR when using containers (#323)
Browse files Browse the repository at this point in the history
* Proper CIRRUS_WORKING_DIR tests

* Respect CIRRUS_WORKING_DIR when using containers

* Expand CIRRUS_WORKING_DIR using task's environment variables

* Properly expand CIRRUS_WORKING_DIR
  • Loading branch information
edigaryev authored Mar 3, 2021
1 parent 040a04b commit f1e6176
Show file tree
Hide file tree
Showing 14 changed files with 213 additions and 99 deletions.
11 changes: 10 additions & 1 deletion internal/executor/build/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/cirruslabs/cirrus-cli/internal/executor/instance"
"github.com/cirruslabs/cirrus-cli/internal/executor/instance/abstract"
"github.com/cirruslabs/cirrus-cli/internal/logger"
"github.com/cirruslabs/cirrus-cli/pkg/parser/expander"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -37,8 +38,16 @@ type Task struct {
}

func NewFromProto(protoTask *api.Task, logger logger.Lightweight) (*Task, error) {
const cirrusWorkingDirVariable = "${CIRRUS_WORKING_DIR}"

customWorkingDir := expander.ExpandEnvironmentVariables(cirrusWorkingDirVariable, protoTask.Environment)
if customWorkingDir == cirrusWorkingDirVariable {
// No expansion was done
customWorkingDir = ""
}

// Create an instance that this task will run on
inst, err := instance.NewFromProto(protoTask.Instance, protoTask.Commands, logger)
inst, err := instance.NewFromProto(protoTask.Instance, protoTask.Commands, customWorkingDir, logger)
if err != nil {
return nil, fmt.Errorf("%w %q: %v", ErrFailedToCreateTask, protoTask.Name, err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ func filesContentsSingleVariation(t *testing.T, dir, dockerfileContents string)

// Extract the resulting container instance's image
for _, task := range result.Tasks {
inst, err := instance.NewFromProto(task.Instance, []*api.Command{}, nil)
inst, err := instance.NewFromProto(task.Instance, []*api.Command{}, "", nil)
if err != nil {
continue
}
Expand Down
21 changes: 17 additions & 4 deletions internal/executor/instance/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"github.com/cirruslabs/cirrus-ci-agent/api"
"github.com/cirruslabs/cirrus-cli/internal/executor/instance/runconfig"
"github.com/cirruslabs/cirrus-cli/internal/executor/platform"
"path"
)

type ContainerInstance struct {
Expand All @@ -14,22 +13,30 @@ type ContainerInstance struct {
Memory uint32
AdditionalContainers []*api.AdditionalContainer
Platform platform.Platform
CustomWorkingDir string
}

func (inst *ContainerInstance) Run(ctx context.Context, config *runconfig.RunConfig) (err error) {
workingVolume, err := CreateWorkingVolumeFromConfig(ctx, config, inst.Platform)
agentVolume, workingVolume, err := CreateWorkingVolumeFromConfig(ctx, config, inst.Platform)
if err != nil {
return err
}
defer func() {
if config.ContainerOptions.NoCleanup {
config.Logger.Infof("not cleaning up agent volume %s, don't forget to remove it with \"docker volume rm %s\"",
agentVolume.Name(), agentVolume.Name())
config.Logger.Infof("not cleaning up working volume %s, don't forget to remove it with \"docker volume rm %s\"",
workingVolume.Name(), workingVolume.Name())

return
}

cleanupErr := workingVolume.Close(config.ContainerBackend)
cleanupErr := agentVolume.Close(config.ContainerBackend)
if err == nil {
err = cleanupErr
}

cleanupErr = workingVolume.Close(config.ContainerBackend)
if err == nil {
err = cleanupErr
}
Expand All @@ -41,12 +48,18 @@ func (inst *ContainerInstance) Run(ctx context.Context, config *runconfig.RunCon
Memory: inst.Memory,
AdditionalContainers: inst.AdditionalContainers,
Platform: inst.Platform,
AgentVolumeName: agentVolume.Name(),
WorkingVolumeName: workingVolume.Name(),
WorkingDirectory: inst.WorkingDirectory(config.ProjectDir, config.DirtyMode),
}

return RunContainerizedAgent(ctx, config, params)
}

func (inst *ContainerInstance) WorkingDirectory(projectDir string, dirtyMode bool) string {
return path.Join(inst.Platform.WorkingVolumeMountpoint(), platform.WorkingVolumeWorkingDir)
if inst.CustomWorkingDir != "" {
return inst.CustomWorkingDir
}

return inst.Platform.GenericWorkingDir()
}
34 changes: 25 additions & 9 deletions internal/executor/instance/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ const (
nano = 1_000_000_000
)

func NewFromProto(anyInstance *any.Any, commands []*api.Command, logger logger.Lightweight) (abstract.Instance, error) {
func NewFromProto(
anyInstance *any.Any,
commands []*api.Command,
customWorkingDir string,
logger logger.Lightweight,
) (abstract.Instance, error) {
if anyInstance == nil {
return nil, fmt.Errorf("%w: got nil instance which means it's probably not supported by the CLI",
ErrFailedToCreateInstance)
Expand Down Expand Up @@ -68,6 +73,7 @@ func NewFromProto(anyInstance *any.Any, commands []*api.Command, logger logger.L
Memory: instance.Memory,
AdditionalContainers: instance.AdditionalContainers,
Platform: containerPlatform,
CustomWorkingDir: customWorkingDir,
}, nil
case *api.PipeInstance:
stages, err := PipeStagesFromCommands(commands)
Expand All @@ -76,9 +82,10 @@ func NewFromProto(anyInstance *any.Any, commands []*api.Command, logger logger.L
}

return &PipeInstance{
CPU: instance.Cpu,
Memory: instance.Memory,
Stages: stages,
CPU: instance.Cpu,
Memory: instance.Memory,
Stages: stages,
CustomWorkingDir: customWorkingDir,
}, nil
case *api.PrebuiltImageInstance:
// PrebuiltImageInstance is currently missing the domain part to craft the full image name
Expand Down Expand Up @@ -120,7 +127,9 @@ type Params struct {
AdditionalContainers []*api.AdditionalContainer
CommandFrom, CommandTo string
Platform platform.Platform
AgentVolumeName string
WorkingVolumeName string
WorkingDirectory string
}

func RunContainerizedAgent(ctx context.Context, config *runconfig.RunConfig, params *Params) error {
Expand Down Expand Up @@ -150,7 +159,7 @@ func RunContainerizedAgent(ctx context.Context, config *runconfig.RunConfig, par
input := containerbackend.ContainerCreateInput{
Image: params.Image,
Entrypoint: []string{
params.Platform.AgentBinaryPath(),
params.Platform.ContainerAgentPath(),
"-api-endpoint",
config.ContainerEndpoint,
"-server-token",
Expand All @@ -168,8 +177,8 @@ func RunContainerizedAgent(ctx context.Context, config *runconfig.RunConfig, par
Mounts: []containerbackend.ContainerMount{
{
Type: containerbackend.MountTypeVolume,
Source: params.WorkingVolumeName,
Target: params.Platform.WorkingVolumeMountpoint(),
Source: params.AgentVolumeName,
Target: params.Platform.ContainerAgentVolumeDir(),
},
},
Resources: containerbackend.ContainerResources{
Expand Down Expand Up @@ -208,12 +217,19 @@ func RunContainerizedAgent(ctx context.Context, config *runconfig.RunConfig, par
})
}

// In dirty mode we mount the project directory in read-write mode
if config.DirtyMode {
// In dirty mode we mount the project directory from host
input.Mounts = append(input.Mounts, containerbackend.ContainerMount{
Type: containerbackend.MountTypeBind,
Source: config.ProjectDir,
Target: path.Join(params.Platform.WorkingVolumeMountpoint(), platform.WorkingVolumeWorkingDir),
Target: params.WorkingDirectory,
})
} else {
// Otherwise we mount the project directory's copy contained in a working volume
input.Mounts = append(input.Mounts, containerbackend.ContainerMount{
Type: containerbackend.MountTypeVolume,
Source: params.WorkingVolumeName,
Target: params.WorkingDirectory,
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (parallels *Parallels) Run(ctx context.Context, config *runconfig.RunConfig
}

func (parallels *Parallels) WorkingDirectory(projectDir string, dirtyMode bool) string {
return platform.NewUnix().WorkingVolumeMountpoint() + platform.WorkingVolumeWorkingDir
return platform.NewUnix().GenericWorkingDir()
}

func TimeSyncCommand(t time.Time) string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func uploadAgent(ctx context.Context, cli *ssh.Client, agentOS, agentVersion str
defer sftpCli.Close()

// Ensure working directory exists
if err := sftpCli.MkdirAll(platform.NewUnix().WorkingVolumeMountpoint()); err != nil {
if err := sftpCli.MkdirAll(platform.NewUnix().CirrusDir()); err != nil {
return "", err
}

Expand All @@ -34,7 +34,7 @@ func uploadAgent(ctx context.Context, cli *ssh.Client, agentOS, agentVersion str
}

// Create agent's binary remotely
remoteAgentPath := path.Join(platform.NewUnix().WorkingVolumeMountpoint(), "cirrus-ci-agent")
remoteAgentPath := path.Join(platform.NewUnix().CirrusDir(), "cirrus-ci-agent")
remoteAgentFile, err := sftpCli.Create(remoteAgentPath)
if err != nil {
return "", err
Expand Down
27 changes: 20 additions & 7 deletions internal/executor/instance/pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/cirruslabs/cirrus-ci-agent/api"
"github.com/cirruslabs/cirrus-cli/internal/executor/instance/runconfig"
"github.com/cirruslabs/cirrus-cli/internal/executor/platform"
"path"
)

var ErrPipeCreationFailed = errors.New("failed to create pipe instance")
Expand All @@ -18,9 +17,10 @@ type PipeStage struct {
}

type PipeInstance struct {
Stages []PipeStage
CPU float32
Memory uint32
Stages []PipeStage
CPU float32
Memory uint32
CustomWorkingDir string
}

// PipeStagesFromCommands uses image hints in commands to build the stages.
Expand Down Expand Up @@ -55,19 +55,26 @@ func PipeStagesFromCommands(commands []*api.Command) ([]PipeStage, error) {
func (pi *PipeInstance) Run(ctx context.Context, config *runconfig.RunConfig) (err error) {
platform := platform.NewUnix()

workingVolume, err := CreateWorkingVolumeFromConfig(ctx, config, platform)
agentVolume, workingVolume, err := CreateWorkingVolumeFromConfig(ctx, config, platform)
if err != nil {
return err
}
defer func() {
if config.ContainerOptions.NoCleanup {
config.Logger.Infof("not cleaning up agent volume %s, don't forget to remove it with \"docker volume rm %s\"",
agentVolume.Name(), agentVolume.Name())
config.Logger.Infof("not cleaning up working volume %s, don't forget to remove it with \"docker volume rm %s\"",
workingVolume.Name(), workingVolume.Name())

return
}

cleanupErr := workingVolume.Close(config.ContainerBackend)
cleanupErr := agentVolume.Close(config.ContainerBackend)
if err == nil {
err = cleanupErr
}

cleanupErr = workingVolume.Close(config.ContainerBackend)
if err == nil {
err = cleanupErr
}
Expand All @@ -81,7 +88,9 @@ func (pi *PipeInstance) Run(ctx context.Context, config *runconfig.RunConfig) (e
CommandFrom: stage.CommandFrom,
CommandTo: stage.CommandTo,
Platform: platform,
AgentVolumeName: agentVolume.Name(),
WorkingVolumeName: workingVolume.Name(),
WorkingDirectory: pi.WorkingDirectory(config.ProjectDir, config.DirtyMode),
}

if err := RunContainerizedAgent(ctx, config, params); err != nil {
Expand All @@ -93,5 +102,9 @@ func (pi *PipeInstance) Run(ctx context.Context, config *runconfig.RunConfig) (e
}

func (pi *PipeInstance) WorkingDirectory(projectDir string, dirtyMode bool) string {
return path.Join(platform.NewUnix().WorkingVolumeMountpoint(), platform.WorkingVolumeWorkingDir)
if pi.CustomWorkingDir != "" {
return pi.CustomWorkingDir
}

return platform.NewUnix().GenericWorkingDir()
}
Loading

0 comments on commit f1e6176

Please sign in to comment.