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

Add support for service containers #1949

Merged
merged 33 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cbc838d
Support services (#42)
Zettat123 Apr 19, 2023
5cc4bd0
Support services options (#45)
Zettat123 Apr 19, 2023
ca623ce
Support intepolation for `env` of `services` (#47)
Zettat123 Apr 20, 2023
7a09a48
Support services `credentials` (#51)
Zettat123 Apr 25, 2023
92f9cc7
Add ContainerMaxLifetime and ContainerNetworkMode options
GuessWhoSamFoo Aug 7, 2023
bcfbd28
Fix container network issue (#56)
sillyguodong May 16, 2023
a168f8f
Check volumes (#60)
Zettat123 Jun 5, 2023
78e4613
Remove ContainerMaxLifetime; fix lint
GuessWhoSamFoo Aug 7, 2023
2cfb56b
Remove unused ValidVolumes
GuessWhoSamFoo Aug 7, 2023
973a5d8
Remove ConnectToNetwork
GuessWhoSamFoo Aug 12, 2023
197d19d
Add docker stubs
GuessWhoSamFoo Aug 12, 2023
83c1304
Close docker clients to prevent file descriptor leaks
GuessWhoSamFoo Aug 12, 2023
fee22cd
Fix the error when removing network in self-hosted mode (#69)
Zettat123 Jun 28, 2023
b84f831
Move service container and network cleanup to rc.cleanUpJobContainer
GuessWhoSamFoo Aug 12, 2023
d0aad7f
Add --network flag; default to host if not using service containers o…
GuessWhoSamFoo Aug 12, 2023
3711e0d
Correctly close executor to prevent fd leak
GuessWhoSamFoo Aug 13, 2023
7480591
Revert to tail instead of full path
GuessWhoSamFoo Aug 14, 2023
f3b7953
fix network duplication
ChristopherHX Aug 22, 2023
e90c543
backport networkingConfig for aliaes
ChristopherHX Aug 22, 2023
779ad2a
don't hardcode netMode host
ChristopherHX Aug 22, 2023
1e8ce80
Convert services test to table driven tests
ZauberNerd Sep 3, 2023
0f0639c
Add failing tests for services
ZauberNerd Sep 3, 2023
5bad75c
Expose service container ports onto the host
ZauberNerd Sep 3, 2023
5f5f3cf
Set container network mode in artifacts server test to host mode
ZauberNerd Sep 3, 2023
10ab2db
Log container network mode when creating/starting a container
ZauberNerd Sep 3, 2023
5ce86a6
fix: Correctly handle ContainerNetworkMode
ChristopherHX Sep 4, 2023
ce9437f
Merge branch 'master' into service-container
ChristopherHX Sep 4, 2023
ed086f2
fix: missing service container network
ChristopherHX Sep 4, 2023
8e5b4f9
Always remove service containers
ZauberNerd Oct 11, 2023
092a41c
Remove networks only if no active endpoints exist
ZauberNerd Oct 11, 2023
41b68eb
Ensure job containers are stopped before starting a new job
ZauberNerd Oct 13, 2023
0f701ec
fix: go build -tags WITHOUT_DOCKER
ChristopherHX Oct 17, 2023
93089ed
Merge branch 'master' into service-container
ZauberNerd Oct 19, 2023
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 @@ -56,6 +56,7 @@ type Input struct {
matrix []string
actionCachePath string
logPrefixJobID bool
networkName string
}

func (i *Input) resolve(path string) string {
Expand Down
3 changes: 3 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/adrg/xdg"
"github.com/andreaskoch/go-fswatch"
docker_container "github.com/docker/docker/api/types/container"
"github.com/joho/godotenv"
gitignore "github.com/sabhiram/go-gitignore"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -96,6 +97,7 @@ func Execute(ctx context.Context, version string) {
rootCmd.PersistentFlags().StringVarP(&input.cacheServerAddr, "cache-server-addr", "", common.GetOutboundIP().String(), "Defines the address to which the cache server binds.")
rootCmd.PersistentFlags().Uint16VarP(&input.cacheServerPort, "cache-server-port", "", 0, "Defines the port where the artifact server listens. 0 means a randomly available port.")
rootCmd.PersistentFlags().StringVarP(&input.actionCachePath, "action-cache-path", "", filepath.Join(CacheHomeDir, "act"), "Defines the path where the actions get cached and host workspaces created.")
rootCmd.PersistentFlags().StringVarP(&input.networkName, "network", "", "host", "Sets a docker network name. Defaults to host.")
ZauberNerd marked this conversation as resolved.
Show resolved Hide resolved
rootCmd.SetArgs(args())

if err := rootCmd.Execute(); err != nil {
Expand Down Expand Up @@ -612,6 +614,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
ReplaceGheActionWithGithubCom: input.replaceGheActionWithGithubCom,
ReplaceGheActionTokenWithGithubCom: input.replaceGheActionTokenWithGithubCom,
Matrix: matrixes,
ContainerNetworkMode: docker_container.NetworkMode(input.networkName),
}
r, err := runner.New(config)
if err != nil {
Expand Down
38 changes: 21 additions & 17 deletions pkg/container/container_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,32 @@ import (
"context"
"io"

"github.com/docker/go-connections/nat"
"github.com/nektos/act/pkg/common"
)

// NewContainerInput the input for the New function
type NewContainerInput struct {
Image string
Username string
Password string
Entrypoint []string
Cmd []string
WorkingDir string
Env []string
Binds []string
Mounts map[string]string
Name string
Stdout io.Writer
Stderr io.Writer
NetworkMode string
Privileged bool
UsernsMode string
Platform string
Options string
Image string
Username string
Password string
Entrypoint []string
Cmd []string
WorkingDir string
Env []string
Binds []string
Mounts map[string]string
Name string
Stdout io.Writer
Stderr io.Writer
NetworkMode string
Privileged bool
UsernsMode string
Platform string
Options string
NetworkAliases []string
ExposedPorts nat.PortSet
PortBindings nat.PortMap
}

// FileEntry is a file to copy to a container
Expand Down
79 changes: 79 additions & 0 deletions pkg/container/docker_network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))

package container

import (
"context"

"github.com/docker/docker/api/types"
"github.com/nektos/act/pkg/common"
)

func NewDockerNetworkCreateExecutor(name string) common.Executor {
GuessWhoSamFoo marked this conversation as resolved.
Show resolved Hide resolved
return func(ctx context.Context) error {
cli, err := GetDockerClient(ctx)
if err != nil {
return err
}
GuessWhoSamFoo marked this conversation as resolved.
Show resolved Hide resolved
defer cli.Close()

// Only create the network if it doesn't exist
networks, err := cli.NetworkList(ctx, types.NetworkListOptions{})
if err != nil {
return err
}
common.Logger(ctx).Debugf("%v", networks)
for _, network := range networks {
if network.Name == name {
common.Logger(ctx).Debugf("Network %v exists", name)
return nil
}

Check warning on line 30 in pkg/container/docker_network.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_network.go#L12-L30

Added lines #L12 - L30 were not covered by tests
}

_, err = cli.NetworkCreate(ctx, name, types.NetworkCreate{
Driver: "bridge",
Scope: "local",
})
if err != nil {
return err
}

Check warning on line 39 in pkg/container/docker_network.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_network.go#L33-L39

Added lines #L33 - L39 were not covered by tests

return nil

Check warning on line 41 in pkg/container/docker_network.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_network.go#L41

Added line #L41 was not covered by tests
}
}

func NewDockerNetworkRemoveExecutor(name string) common.Executor {
GuessWhoSamFoo marked this conversation as resolved.
Show resolved Hide resolved
return func(ctx context.Context) error {
cli, err := GetDockerClient(ctx)
if err != nil {
return err
}
GuessWhoSamFoo marked this conversation as resolved.
Show resolved Hide resolved
defer cli.Close()

// Make shure that all network of the specified name are removed
// cli.NetworkRemove refuses to remove a network if there are duplicates
networks, err := cli.NetworkList(ctx, types.NetworkListOptions{})
if err != nil {
return err
}
common.Logger(ctx).Debugf("%v", networks)
for _, network := range networks {
if network.Name == name {
result, err := cli.NetworkInspect(ctx, network.ID, types.NetworkInspectOptions{})
if err != nil {
return err
}

Check warning on line 65 in pkg/container/docker_network.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_network.go#L45-L65

Added lines #L45 - L65 were not covered by tests

if len(result.Containers) == 0 {
if err = cli.NetworkRemove(ctx, network.ID); err != nil {
common.Logger(ctx).Debugf("%v", err)
}
} else {
common.Logger(ctx).Debugf("Refusing to remove network %v because it still has active endpoints", name)
}

Check warning on line 73 in pkg/container/docker_network.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_network.go#L67-L73

Added lines #L67 - L73 were not covered by tests
}
}

return err

Check warning on line 77 in pkg/container/docker_network.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_network.go#L77

Added line #L77 was not covered by tests
}
}
50 changes: 34 additions & 16 deletions pkg/container/docker_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
specs "github.com/opencontainers/image-spec/specs-go/v1"
Expand Down Expand Up @@ -66,7 +67,7 @@

func (cr *containerReference) Create(capAdd []string, capDrop []string) common.Executor {
return common.
NewInfoExecutor("%sdocker create image=%s platform=%s entrypoint=%+q cmd=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd).
NewInfoExecutor("%sdocker create image=%s platform=%s entrypoint=%+q cmd=%+q network=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd, cr.input.NetworkMode).

Check warning on line 70 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L70

Added line #L70 was not covered by tests
Then(
common.NewPipelineExecutor(
cr.connect(),
Expand All @@ -78,7 +79,7 @@

func (cr *containerReference) Start(attach bool) common.Executor {
return common.
NewInfoExecutor("%sdocker run image=%s platform=%s entrypoint=%+q cmd=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd).
NewInfoExecutor("%sdocker run image=%s platform=%s entrypoint=%+q cmd=%+q network=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd, cr.input.NetworkMode).

Check warning on line 82 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L82

Added line #L82 was not covered by tests
Then(
common.NewPipelineExecutor(
cr.connect(),
Expand Down Expand Up @@ -240,8 +241,8 @@

archMapper := map[string]string{
"x86_64": "X64",
"386": "X86",
"aarch64": "ARM64",

Check warning on line 245 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L244-L245

Added lines #L244 - L245 were not covered by tests
}
if arch, ok := archMapper[info.Architecture]; ok {
return arch
Expand Down Expand Up @@ -345,13 +346,13 @@
return nil, nil, fmt.Errorf("Cannot parse container options: '%s': '%w'", input.Options, err)
}

if len(copts.netMode.Value()) == 0 {
if err = copts.netMode.Set("host"); err != nil {
return nil, nil, fmt.Errorf("Cannot parse networkmode=host. This is an internal error and should not happen: '%w'", err)
if err = copts.netMode.Set(cr.input.NetworkMode); err != nil {
return nil, nil, fmt.Errorf("Cannot parse networkmode=%s. This is an internal error and should not happen: '%w'", cr.input.NetworkMode, err)
}

Check warning on line 352 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L349-L352

Added lines #L349 - L352 were not covered by tests
}

containerConfig, err := parse(flags, copts, runtime.GOOS)

Check warning on line 355 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L355

Added line #L355 was not covered by tests
if err != nil {
return nil, nil, fmt.Errorf("Cannot process container options: '%s': '%w'", input.Options, err)
}
Expand Down Expand Up @@ -391,10 +392,11 @@
input := cr.input

config := &container.Config{
Image: input.Image,
WorkingDir: input.WorkingDir,
Env: input.Env,
Tty: isTerminal,
Image: input.Image,
WorkingDir: input.WorkingDir,
Env: input.Env,
ExposedPorts: input.ExposedPorts,
Tty: isTerminal,

Check warning on line 399 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L395-L399

Added lines #L395 - L399 were not covered by tests
}
logger.Debugf("Common container.Config ==> %+v", config)

Expand Down Expand Up @@ -430,13 +432,14 @@
}

hostConfig := &container.HostConfig{
CapAdd: capAdd,
CapDrop: capDrop,
Binds: input.Binds,
Mounts: mounts,
NetworkMode: container.NetworkMode(input.NetworkMode),
Privileged: input.Privileged,
UsernsMode: container.UsernsMode(input.UsernsMode),
CapAdd: capAdd,
CapDrop: capDrop,
Binds: input.Binds,
Mounts: mounts,
NetworkMode: container.NetworkMode(input.NetworkMode),
Privileged: input.Privileged,
UsernsMode: container.UsernsMode(input.UsernsMode),
PortBindings: input.PortBindings,

Check warning on line 442 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L435-L442

Added lines #L435 - L442 were not covered by tests
}
logger.Debugf("Common container.HostConfig ==> %+v", hostConfig)

Expand All @@ -445,7 +448,22 @@
return err
}

resp, err := cr.cli.ContainerCreate(ctx, config, hostConfig, nil, platSpecs, input.Name)
var networkingConfig *network.NetworkingConfig
logger.Debugf("input.NetworkAliases ==> %v", input.NetworkAliases)
if hostConfig.NetworkMode.IsUserDefined() && len(input.NetworkAliases) > 0 {
endpointConfig := &network.EndpointSettings{
Aliases: input.NetworkAliases,
}
networkingConfig = &network.NetworkingConfig{
EndpointsConfig: map[string]*network.EndpointSettings{
input.NetworkMode: endpointConfig,
},
}
} else {
logger.Debugf("not a use defined config??")
}

Check warning on line 464 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L451-L464

Added lines #L451 - L464 were not covered by tests

resp, err := cr.cli.ContainerCreate(ctx, config, hostConfig, networkingConfig, platSpecs, input.Name)

Check warning on line 466 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L466

Added line #L466 was not covered by tests
if err != nil {
return fmt.Errorf("failed to create container: '%w'", err)
}
Expand Down Expand Up @@ -586,7 +604,7 @@
}
exp := regexp.MustCompile(`\d+\n`)
found := exp.FindString(sid)
id, err := strconv.ParseInt(strings.TrimSpace(found), 10, 32)

Check warning on line 607 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L607

Added line #L607 was not covered by tests
if err != nil {
return nil
}
Expand Down Expand Up @@ -649,12 +667,12 @@
}
}

func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error {
err := cr.cli.CopyToContainer(ctx, cr.id, destPath, tarStream, types.CopyToContainerOptions{})
if err != nil {
return fmt.Errorf("failed to copy content to container: %w", err)
}
return nil

Check warning on line 675 in pkg/container/docker_run.go

View check run for this annotation

Codecov / codecov/patch

pkg/container/docker_run.go#L670-L675

Added lines #L670 - L675 were not covered by tests
}

func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgnore bool) common.Executor {
Expand Down
1 change: 1 addition & 0 deletions pkg/container/docker_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func TestDocker(t *testing.T) {
ctx := context.Background()
client, err := GetDockerClient(ctx)
assert.NoError(t, err)
defer client.Close()

dockerBuild := NewDockerBuildExecutor(NewDockerBuildExecutorInput{
ContextDir: "testdata",
Expand Down
12 changes: 12 additions & 0 deletions pkg/container/docker_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,15 @@ func NewDockerVolumeRemoveExecutor(volume string, force bool) common.Executor {
return nil
}
}

func NewDockerNetworkCreateExecutor(name string) common.Executor {
return func(ctx context.Context) error {
return nil
}
}

func NewDockerNetworkRemoveExecutor(name string) common.Executor {
return func(ctx context.Context) error {
return nil
}
}
10 changes: 8 additions & 2 deletions pkg/runner/job_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
result(result string)
}

//nolint:contextcheck,gocyclo
func newJobExecutor(info jobInfo, sf stepFactory, rc *RunContext) common.Executor {
steps := make([]common.Executor, 0)
preSteps := make([]common.Executor, 0)
Expand Down Expand Up @@ -87,7 +88,7 @@

postExec := useStepLogger(rc, stepModel, stepStagePost, step.post())
if postExecutor != nil {
// run the post exector in reverse order
// run the post executor in reverse order
postExecutor = postExec.Finally(postExecutor)
} else {
postExecutor = postExec
Expand All @@ -101,7 +102,12 @@
// always allow 1 min for stopping and removing the runner, even if we were cancelled
ctx, cancel := context.WithTimeout(common.WithLogger(context.Background(), common.Logger(ctx)), time.Minute)
defer cancel()
err = info.stopContainer()(ctx) //nolint:contextcheck

logger := common.Logger(ctx)
logger.Infof("Cleaning up container for job %s", rc.JobName)
if err = info.stopContainer()(ctx); err != nil {
logger.Errorf("Error while stop job container: %v", err)
}

Check warning on line 110 in pkg/runner/job_executor.go

View check run for this annotation

Codecov / codecov/patch

pkg/runner/job_executor.go#L109-L110

Added lines #L109 - L110 were not covered by tests
}
setJobResult(ctx, info, rc, jobError == nil)
setJobOutputs(ctx, rc)
Expand Down
Loading
Loading