Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 cmd/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Input struct {
autodetectEvent bool
eventPath string
reuseContainers bool
uniqueContainerNames bool
bindWorkdir bool
secrets []string
vars []string
Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func createRootCommand(ctx context.Context, input *Input, version string) *cobra
rootCmd.Flags().StringArrayVarP(&input.inputs, "input", "", []string{}, "action input to make available to actions (e.g. --input myinput=foo)")
rootCmd.Flags().StringArrayVarP(&input.platforms, "platform", "P", []string{}, "custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04)")
rootCmd.Flags().BoolVarP(&input.reuseContainers, "reuse", "r", false, "don't remove container(s) on successfully completed workflow(s) to maintain state between runs")
rootCmd.Flags().BoolVarP(&input.uniqueContainerNames, "unique-container-names", "", false, "container names will include a (quasi-)unique (random) component to avoid conflicts with other instances of act running against the same Docker server (disabled if --reuse is present)")
rootCmd.Flags().BoolVarP(&input.bindWorkdir, "bind", "b", false, "bind working directory to container, rather than copy")
rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", true, "pull docker image(s) even if already present")
rootCmd.Flags().BoolVarP(&input.forceRebuild, "rebuild", "", true, "rebuild local action docker image(s) even if already present")
Expand Down Expand Up @@ -611,6 +612,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
ForcePull: !input.actionOfflineMode && input.forcePull,
ForceRebuild: input.forceRebuild,
ReuseContainers: input.reuseContainers,
UniqueContainerNames: input.uniqueContainerNames,
Workdir: input.Workdir(),
ActionCacheDir: input.actionCachePath,
ActionOfflineMode: input.actionOfflineMode,
Expand Down
42 changes: 42 additions & 0 deletions pkg/runner/run_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"errors"
"fmt"
"io"
"math/big"
"os"
"path/filepath"
"regexp"
Expand All @@ -28,6 +29,8 @@ import (
"github.com/opencontainers/selinux/go-selinux"
)

var uniqueIdentifier = ""

// RunContext contains info about current job
type RunContext struct {
Name string
Expand Down Expand Up @@ -55,6 +58,38 @@ type RunContext struct {
nodeToolFullPath string
}

func init() {
var charset = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

// To allow multiple concurrent instances of ACT, we need to use
// a unique identifier that will differentiate all containers
// spawned by this instance from those spawned by others. If we
// don't do that, then weird conflicts will arise due to possible
// container name collsions. We use a secure random string of
// alphanumeric characters for this purpose.
//
// We don't use a UUID due to its length - we don't want too long
// a string ... just long enough to provide sufficient randomness
//
// This function will try to generate a secure random string. If
// that fails for some reason, it will use the PID as its
// "unique identifier"
length64 := int64(len(charset))
target := make([]rune, 8)
for i := range target {
p, err := rand.Int(rand.Reader, big.NewInt(length64))
if err != nil {
// TODO: Should we log this error? How (without a lot of hoops)?
// If we had some sort of issue with the randomness,
// let's just use the PID as the "randomness"
uniqueIdentifier = fmt.Sprintf("%08X", os.Getpid())
return
}
target[i] = charset[p.Int64()]
}
uniqueIdentifier = string(target)
}

func (rc *RunContext) AddMask(mask string) {
rc.Masks = append(rc.Masks, mask)
}
Expand Down Expand Up @@ -90,6 +125,13 @@ func (rc *RunContext) GetEnv() map[string]string {
}

func (rc *RunContext) jobContainerName() string {
// Assume we want to use the consistent, constant identifier
if rc.Config.UniqueContainerNames && !rc.Config.ReuseContainers {
// If we're not going to reuse the containers, and we want to be
// able to run multiple instances of act concurrently, then we
// must use the instance-specific (random) identifier
return createContainerName("act", uniqueIdentifier, rc.String())
}
return createContainerName("act", rc.String())
}

Expand Down
1 change: 1 addition & 0 deletions pkg/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Config struct {
EventPath string // path to JSON file to use for event.json in containers
DefaultBranch string // name of the main branch for this repository
ReuseContainers bool // reuse containers to maintain state
UniqueContainerNames bool // make container names for this instance unique
ForcePull bool // force pulling of the image, even if already present
ForceRebuild bool // force rebuilding local docker image action
LogOutput bool // log the output from docker run
Expand Down