2024-01-08 13:26:03 -06:00
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
2023-01-10 16:08:57 -06:00
2019-01-12 22:45:25 -06:00
package container
import (
2020-02-23 17:01:25 -06:00
"archive/tar"
"bytes"
2019-01-12 22:45:25 -06:00
"context"
2022-08-07 01:07:54 -05:00
"errors"
2019-01-12 22:45:25 -06:00
"fmt"
"io"
"os"
2020-02-24 18:38:49 -06:00
"path/filepath"
2021-01-12 00:39:43 -06:00
"regexp"
2021-01-12 00:41:35 -06:00
"runtime"
2022-06-20 17:47:39 -05:00
"strconv"
2021-01-14 23:37:38 -06:00
"strings"
2019-01-12 22:45:25 -06:00
2023-10-31 09:32:21 -05:00
"github.com/Masterminds/semver"
2023-06-05 04:21:59 -05:00
"github.com/docker/cli/cli/compose/loader"
2020-05-03 23:15:42 -05:00
"github.com/docker/cli/cli/connhelper"
2019-01-12 22:45:25 -06:00
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
2020-02-23 17:01:25 -06:00
"github.com/docker/docker/api/types/mount"
2023-10-31 09:32:21 -05:00
"github.com/docker/docker/api/types/network"
networktypes "github.com/docker/docker/api/types/network"
2019-01-12 22:45:25 -06:00
"github.com/docker/docker/client"
2020-02-11 11:10:35 -06:00
"github.com/docker/docker/pkg/stdcopy"
2023-10-31 09:32:21 -05:00
"github.com/go-git/go-billy/v5/helper/polyfill"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/gobwas/glob"
"github.com/imdario/mergo"
"github.com/joho/godotenv"
"github.com/kballard/go-shellquote"
2021-03-28 23:08:40 -05:00
specs "github.com/opencontainers/image-spec/specs-go/v1"
2023-10-31 09:32:21 -05:00
"github.com/spf13/pflag"
2021-01-12 00:39:43 -06:00
"golang.org/x/term"
2021-03-28 23:08:40 -05:00
"github.com/nektos/act/pkg/common"
2024-01-29 18:46:45 -06:00
"github.com/nektos/act/pkg/filecollector"
2019-01-12 22:45:25 -06:00
)
2020-02-23 17:01:25 -06:00
// NewContainer creates a reference to a container
2022-11-16 15:29:45 -06:00
func NewContainer ( input * NewContainerInput ) ExecutionsEnvironment {
2020-02-07 00:17:58 -06:00
cr := new ( containerReference )
cr . input = input
2020-02-23 17:01:25 -06:00
return cr
}
2020-02-07 00:17:58 -06:00
2023-04-18 22:23:28 -05:00
func ( cr * containerReference ) ConnectToNetwork ( name string ) common . Executor {
return common .
NewDebugExecutor ( "%sdocker network connect %s %s" , logPrefix , name , cr . input . Name ) .
Then (
common . NewPipelineExecutor (
cr . connect ( ) ,
cr . connectToNetwork ( name , cr . input . NetworkAliases ) ,
) . IfNot ( common . Dryrun ) ,
)
}
func ( cr * containerReference ) connectToNetwork ( name string , aliases [ ] string ) common . Executor {
return func ( ctx context . Context ) error {
return cr . cli . NetworkConnect ( ctx , name , cr . input . Name , & networktypes . EndpointSettings {
Aliases : aliases ,
} )
}
}
2021-03-30 12:10:42 -05:00
// supportsContainerImagePlatform returns true if the underlying Docker server
// API version is 1.41 and beyond
2022-05-24 09:52:25 -05:00
func supportsContainerImagePlatform ( ctx context . Context , cli client . APIClient ) bool {
2021-03-30 12:10:42 -05:00
logger := common . Logger ( ctx )
ver , err := cli . ServerVersion ( ctx )
if err != nil {
logger . Panicf ( "Failed to get Docker API Version: %s" , err )
return false
}
sv , err := semver . NewVersion ( ver . APIVersion )
if err != nil {
logger . Panicf ( "Failed to unmarshal Docker Version: %s" , err )
return false
}
constraint , _ := semver . NewConstraint ( ">= 1.41" )
return constraint . Check ( sv )
}
2021-06-04 11:06:59 -05:00
func ( cr * containerReference ) Create ( capAdd [ ] string , capDrop [ ] string ) common . Executor {
2020-02-07 00:17:58 -06:00
return common .
Add support for service containers (#1949)
* Support services (#42)
Removed createSimpleContainerName and AutoRemove flag
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services options (#45)
Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support intepolation for `env` of `services` (#47)
Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services `credentials` (#51)
If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: https://gitea.com/gitea/act/src/commit/0c1f2edb996a87ee17dcf3cfa7259c04be02abd7/pkg/runner/run_context.go#L228-L269
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Add ContainerMaxLifetime and ContainerNetworkMode options
from: https://gitea.com/gitea/act/commit/b9c20dcaa43899cb3bb327619d447248303170e0
* Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
* Check volumes (#60)
This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.
Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)
In addition, volumes specified by `options` will also be checked.
Currently, the following default volumes (see https://gitea.com/gitea/act/src/commit/a72822b3f83d3e68ffc697101b713b7badf57e2f/pkg/runner/run_context.go#L116-L166) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Remove ContainerMaxLifetime; fix lint
* Remove unused ValidVolumes
* Remove ConnectToNetwork
* Add docker stubs
* Close docker clients to prevent file descriptor leaks
* Fix the error when removing network in self-hosted mode (#69)
Fixes https://gitea.com/gitea/act_runner/issues/255
Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Move service container and network cleanup to rc.cleanUpJobContainer
* Add --network flag; default to host if not using service containers or set explicitly
* Correctly close executor to prevent fd leak
* Revert to tail instead of full path
* fix network duplication
* backport networkingConfig for aliaes
* don't hardcode netMode host
* Convert services test to table driven tests
* Add failing tests for services
* Expose service container ports onto the host
* Set container network mode in artifacts server test to host mode
* Log container network mode when creating/starting a container
* fix: Correctly handle ContainerNetworkMode
* fix: missing service container network
* Always remove service containers
Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.
* Remove networks only if no active endpoints exist
* Ensure job containers are stopped before starting a new job
* fix: go build -tags WITHOUT_DOCKER
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 04:24:52 -05:00
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 ) .
2020-02-07 00:17:58 -06:00
Then (
common . NewPipelineExecutor (
cr . connect ( ) ,
cr . find ( ) ,
2021-06-04 11:06:59 -05:00
cr . create ( capAdd , capDrop ) ,
2020-02-23 17:01:25 -06:00
) . IfNot ( common . Dryrun ) ,
)
}
2021-08-10 14:40:20 -05:00
2020-02-23 17:01:25 -06:00
func ( cr * containerReference ) Start ( attach bool ) common . Executor {
return common .
Add support for service containers (#1949)
* Support services (#42)
Removed createSimpleContainerName and AutoRemove flag
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services options (#45)
Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support intepolation for `env` of `services` (#47)
Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services `credentials` (#51)
If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: https://gitea.com/gitea/act/src/commit/0c1f2edb996a87ee17dcf3cfa7259c04be02abd7/pkg/runner/run_context.go#L228-L269
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Add ContainerMaxLifetime and ContainerNetworkMode options
from: https://gitea.com/gitea/act/commit/b9c20dcaa43899cb3bb327619d447248303170e0
* Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
* Check volumes (#60)
This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.
Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)
In addition, volumes specified by `options` will also be checked.
Currently, the following default volumes (see https://gitea.com/gitea/act/src/commit/a72822b3f83d3e68ffc697101b713b7badf57e2f/pkg/runner/run_context.go#L116-L166) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Remove ContainerMaxLifetime; fix lint
* Remove unused ValidVolumes
* Remove ConnectToNetwork
* Add docker stubs
* Close docker clients to prevent file descriptor leaks
* Fix the error when removing network in self-hosted mode (#69)
Fixes https://gitea.com/gitea/act_runner/issues/255
Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Move service container and network cleanup to rc.cleanUpJobContainer
* Add --network flag; default to host if not using service containers or set explicitly
* Correctly close executor to prevent fd leak
* Revert to tail instead of full path
* fix network duplication
* backport networkingConfig for aliaes
* don't hardcode netMode host
* Convert services test to table driven tests
* Add failing tests for services
* Expose service container ports onto the host
* Set container network mode in artifacts server test to host mode
* Log container network mode when creating/starting a container
* fix: Correctly handle ContainerNetworkMode
* fix: missing service container network
* Always remove service containers
Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.
* Remove networks only if no active endpoints exist
* Ensure job containers are stopped before starting a new job
* fix: go build -tags WITHOUT_DOCKER
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 04:24:52 -05:00
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 ) .
2020-02-23 17:01:25 -06:00
Then (
common . NewPipelineExecutor (
cr . connect ( ) ,
cr . find ( ) ,
cr . attach ( ) . IfBool ( attach ) ,
2020-02-07 00:17:58 -06:00
cr . start ( ) ,
2020-02-23 17:01:25 -06:00
cr . wait ( ) . IfBool ( attach ) ,
2022-06-20 17:47:39 -05:00
cr . tryReadUID ( ) ,
cr . tryReadGID ( ) ,
func ( ctx context . Context ) error {
// If this fails, then folders have wrong permissions on non root container
2022-07-08 17:39:42 -05:00
if cr . UID != 0 || cr . GID != 0 {
_ = cr . Exec ( [ ] string { "chown" , "-R" , fmt . Sprintf ( "%d:%d" , cr . UID , cr . GID ) , cr . input . WorkingDir } , nil , "0" , "" ) ( ctx )
}
2022-06-20 17:47:39 -05:00
return nil
} ,
2020-02-07 00:17:58 -06:00
) . IfNot ( common . Dryrun ) ,
)
}
2021-08-10 14:40:20 -05:00
2020-02-23 17:01:25 -06:00
func ( cr * containerReference ) Pull ( forcePull bool ) common . Executor {
2021-08-10 14:40:20 -05:00
return common .
NewInfoExecutor ( "%sdocker pull image=%s platform=%s username=%s forcePull=%t" , logPrefix , cr . input . Image , cr . input . Platform , cr . input . Username , forcePull ) .
Then (
NewDockerPullExecutor ( NewDockerPullExecutorInput {
Image : cr . input . Image ,
ForcePull : forcePull ,
Platform : cr . input . Platform ,
Username : cr . input . Username ,
Password : cr . input . Password ,
} ) ,
)
2020-02-23 17:01:25 -06:00
}
2021-05-05 18:11:43 -05:00
2020-02-23 17:01:25 -06:00
func ( cr * containerReference ) Copy ( destPath string , files ... * FileEntry ) common . Executor {
return common . NewPipelineExecutor (
cr . connect ( ) ,
cr . find ( ) ,
cr . copyContent ( destPath , files ... ) ,
) . IfNot ( common . Dryrun )
}
2021-05-03 09:37:20 -05:00
func ( cr * containerReference ) CopyDir ( destPath string , srcPath string , useGitIgnore bool ) common . Executor {
2020-02-24 18:38:49 -06:00
return common . NewPipelineExecutor (
2020-02-24 19:48:21 -06:00
common . NewInfoExecutor ( "%sdocker cp src=%s dst=%s" , logPrefix , srcPath , destPath ) ,
2021-05-03 09:37:20 -05:00
cr . copyDir ( destPath , srcPath , useGitIgnore ) ,
2022-06-20 17:47:39 -05:00
func ( ctx context . Context ) error {
// If this fails, then folders have wrong permissions on non root container
2022-07-08 17:39:42 -05:00
if cr . UID != 0 || cr . GID != 0 {
_ = cr . Exec ( [ ] string { "chown" , "-R" , fmt . Sprintf ( "%d:%d" , cr . UID , cr . GID ) , destPath } , nil , "0" , "" ) ( ctx )
}
2022-06-20 17:47:39 -05:00
return nil
} ,
2020-02-24 18:38:49 -06:00
) . IfNot ( common . Dryrun )
}
2021-08-03 12:39:56 -05:00
func ( cr * containerReference ) GetContainerArchive ( ctx context . Context , srcPath string ) ( io . ReadCloser , error ) {
2022-05-11 14:14:45 -05:00
if common . Dryrun ( ctx ) {
return nil , fmt . Errorf ( "DRYRUN is not supported in GetContainerArchive" )
}
2021-08-03 12:39:56 -05:00
a , _ , err := cr . cli . CopyFromContainer ( ctx , cr . id , srcPath )
return a , err
}
2021-05-05 18:11:43 -05:00
func ( cr * containerReference ) UpdateFromEnv ( srcPath string , env * map [ string ] string ) common . Executor {
2022-12-06 10:19:27 -06:00
return parseEnvFile ( cr , srcPath , env ) . IfNot ( common . Dryrun )
2021-01-12 00:39:43 -06:00
}
2021-09-27 14:01:14 -05:00
func ( cr * containerReference ) UpdateFromImageEnv ( env * map [ string ] string ) common . Executor {
return cr . extractFromImageEnv ( env ) . IfNot ( common . Dryrun )
}
2021-08-10 14:40:20 -05:00
func ( cr * containerReference ) Exec ( command [ ] string , env map [ string ] string , user , workdir string ) common . Executor {
2020-02-23 17:01:25 -06:00
return common . NewPipelineExecutor (
2021-08-10 14:40:20 -05:00
common . NewInfoExecutor ( "%sdocker exec cmd=[%s] user=%s workdir=%s" , logPrefix , strings . Join ( command , " " ) , user , workdir ) ,
2020-02-23 17:01:25 -06:00
cr . connect ( ) ,
cr . find ( ) ,
2021-08-10 14:40:20 -05:00
cr . exec ( command , env , user , workdir ) ,
2020-02-23 17:01:25 -06:00
) . IfNot ( common . Dryrun )
}
2021-05-05 18:11:43 -05:00
2020-02-23 17:01:25 -06:00
func ( cr * containerReference ) Remove ( ) common . Executor {
return common . NewPipelineExecutor (
cr . connect ( ) ,
cr . find ( ) ,
) . Finally (
cr . remove ( ) ,
) . IfNot ( common . Dryrun )
}
2019-01-12 22:45:25 -06:00
2022-05-11 14:06:05 -05:00
func ( cr * containerReference ) ReplaceLogWriter ( stdout io . Writer , stderr io . Writer ) ( io . Writer , io . Writer ) {
out := cr . input . Stdout
err := cr . input . Stderr
cr . input . Stdout = stdout
cr . input . Stderr = stderr
return out , err
}
2020-02-07 00:17:58 -06:00
type containerReference struct {
2022-05-24 09:52:25 -05:00
cli client . APIClient
2020-02-07 00:17:58 -06:00
id string
2020-02-23 17:01:25 -06:00
input * NewContainerInput
2022-06-20 17:47:39 -05:00
UID int
GID int
2022-11-16 15:29:45 -06:00
LinuxContainerEnvironmentExtensions
2020-02-07 00:17:58 -06:00
}
2019-01-12 22:45:25 -06:00
2022-05-24 09:52:25 -05:00
func GetDockerClient ( ctx context . Context ) ( cli client . APIClient , err error ) {
2020-05-03 23:15:42 -05:00
dockerHost := os . Getenv ( "DOCKER_HOST" )
if strings . HasPrefix ( dockerHost , "ssh://" ) {
var helper * connhelper . ConnectionHelper
helper , err = connhelper . GetConnectionHelper ( dockerHost )
if err != nil {
return nil , err
}
cli , err = client . NewClientWithOpts (
client . WithHost ( helper . Host ) ,
client . WithDialContext ( helper . Dialer ) ,
)
} else {
cli , err = client . NewClientWithOpts ( client . FromEnv )
}
if err != nil {
2022-06-10 16:16:42 -05:00
return nil , fmt . Errorf ( "failed to connect to docker daemon: %w" , err )
2020-05-03 23:15:42 -05:00
}
cli . NegotiateAPIVersion ( ctx )
2022-06-10 16:16:42 -05:00
return cli , nil
2020-05-03 23:15:42 -05:00
}
2022-03-22 14:26:10 -05:00
func GetHostInfo ( ctx context . Context ) ( info types . Info , err error ) {
2022-05-24 09:52:25 -05:00
var cli client . APIClient
2022-03-22 14:26:10 -05:00
cli , err = GetDockerClient ( ctx )
if err != nil {
return info , err
}
defer cli . Close ( )
info , err = cli . Info ( ctx )
if err != nil {
return info , err
}
return info , nil
}
2022-08-29 10:39:31 -05:00
// Arch fetches values from docker info and translates architecture to
// GitHub actions compatible runner.arch values
// https://github.com/github/docs/blob/main/data/reusables/actions/runner-arch-description.md
func RunnerArch ( ctx context . Context ) string {
info , err := GetHostInfo ( ctx )
if err != nil {
return ""
}
archMapper := map [ string ] string {
"x86_64" : "X64" ,
2024-01-23 20:44:48 -06:00
"amd64" : "X64" ,
2023-05-23 07:26:47 -05:00
"386" : "X86" ,
"aarch64" : "ARM64" ,
2024-01-23 20:44:48 -06:00
"arm64" : "ARM64" ,
2022-08-29 10:39:31 -05:00
}
if arch , ok := archMapper [ info . Architecture ] ; ok {
return arch
}
return info . Architecture
}
2020-02-07 00:17:58 -06:00
func ( cr * containerReference ) connect ( ) common . Executor {
return func ( ctx context . Context ) error {
2020-02-23 17:01:25 -06:00
if cr . cli != nil {
return nil
}
2020-05-03 23:15:42 -05:00
cli , err := GetDockerClient ( ctx )
2019-01-12 22:45:25 -06:00
if err != nil {
2020-05-03 23:15:42 -05:00
return err
2019-01-12 22:45:25 -06:00
}
2020-02-07 00:17:58 -06:00
cr . cli = cli
return nil
}
}
2019-01-12 22:45:25 -06:00
2021-10-24 11:50:43 -05:00
func ( cr * containerReference ) Close ( ) common . Executor {
return func ( ctx context . Context ) error {
if cr . cli != nil {
2022-06-10 16:16:42 -05:00
err := cr . cli . Close ( )
2021-10-24 11:50:43 -05:00
cr . cli = nil
2022-06-10 16:16:42 -05:00
if err != nil {
return fmt . Errorf ( "failed to close client: %w" , err )
}
2021-10-24 11:50:43 -05:00
}
return nil
}
}
2020-02-07 00:17:58 -06:00
func ( cr * containerReference ) find ( ) common . Executor {
return func ( ctx context . Context ) error {
2020-02-23 17:01:25 -06:00
if cr . id != "" {
return nil
}
2020-02-07 00:17:58 -06:00
containers , err := cr . cli . ContainerList ( ctx , types . ContainerListOptions {
All : true ,
} )
2019-01-12 22:45:25 -06:00
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to list containers: %w" , err )
2019-01-12 22:45:25 -06:00
}
2021-01-12 00:39:43 -06:00
for _ , c := range containers {
for _ , name := range c . Names {
2020-02-07 00:17:58 -06:00
if name [ 1 : ] == cr . input . Name {
2021-01-12 00:39:43 -06:00
cr . id = c . ID
2020-02-07 00:17:58 -06:00
return nil
}
2019-01-17 02:45:37 -06:00
}
2019-01-12 22:45:25 -06:00
}
2020-02-07 00:17:58 -06:00
cr . id = ""
return nil
2019-01-12 22:45:25 -06:00
}
}
2020-02-07 00:17:58 -06:00
func ( cr * containerReference ) remove ( ) common . Executor {
return func ( ctx context . Context ) error {
if cr . id == "" {
return nil
}
2019-01-12 22:45:25 -06:00
2020-02-07 00:17:58 -06:00
logger := common . Logger ( ctx )
2021-12-22 11:29:43 -06:00
err := cr . cli . ContainerRemove ( ctx , cr . id , types . ContainerRemoveOptions {
2020-02-07 00:17:58 -06:00
RemoveVolumes : true ,
Force : true ,
} )
if err != nil {
2022-06-10 16:16:42 -05:00
logger . Error ( fmt . Errorf ( "failed to remove container: %w" , err ) )
2019-01-12 22:45:25 -06:00
}
2020-02-07 00:17:58 -06:00
logger . Debugf ( "Removed container: %v" , cr . id )
2020-02-23 18:36:44 -06:00
cr . id = ""
2020-02-07 00:17:58 -06:00
return nil
2019-01-12 22:45:25 -06:00
}
}
2022-10-12 11:30:56 -05:00
func ( cr * containerReference ) mergeContainerConfigs ( ctx context . Context , config * container . Config , hostConfig * container . HostConfig ) ( * container . Config , * container . HostConfig , error ) {
logger := common . Logger ( ctx )
input := cr . input
if input . Options == "" {
return config , hostConfig , nil
}
// parse configuration from CLI container.options
flags := pflag . NewFlagSet ( "container_flags" , pflag . ContinueOnError )
copts := addFlags ( flags )
optionsArgs , err := shellquote . Split ( input . Options )
if err != nil {
return nil , nil , fmt . Errorf ( "Cannot split container options: '%s': '%w'" , input . Options , err )
}
err = flags . Parse ( optionsArgs )
if err != nil {
return nil , nil , fmt . Errorf ( "Cannot parse container options: '%s': '%w'" , input . Options , err )
}
2023-05-09 03:41:31 -05:00
// If a service container's network is set to `host`, the container will not be able to
// connect to the specified network created for the job container and the service containers.
// So comment out the following code.
// if len(copts.netMode.Value()) == 0 {
2024-02-16 22:28:10 -06:00
// 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)
2023-05-09 03:41:31 -05:00
// }
// }
2023-04-18 09:37:59 -05:00
2023-05-15 22:21:18 -05:00
// If the `privileged` config has been disabled, `copts.privileged` need to be forced to false,
// even if the user specifies `--privileged` in the options string.
if ! hostConfig . Privileged {
copts . privileged = false
}
2023-08-09 13:21:05 -05:00
containerConfig , err := parse ( flags , copts , runtime . GOOS )
2022-10-12 11:30:56 -05:00
if err != nil {
return nil , nil , fmt . Errorf ( "Cannot process container options: '%s': '%w'" , input . Options , err )
}
logger . Debugf ( "Custom container.Config from options ==> %+v" , containerConfig . Config )
2023-05-31 05:33:39 -05:00
err = mergo . Merge ( config , containerConfig . Config , mergo . WithOverride , mergo . WithAppendSlice )
2022-10-12 11:30:56 -05:00
if err != nil {
return nil , nil , fmt . Errorf ( "Cannot merge container.Config options: '%s': '%w'" , input . Options , err )
}
logger . Debugf ( "Merged container.Config ==> %+v" , config )
logger . Debugf ( "Custom container.HostConfig from options ==> %+v" , containerConfig . HostConfig )
2022-12-06 09:58:47 -06:00
hostConfig . Binds = append ( hostConfig . Binds , containerConfig . HostConfig . Binds ... )
hostConfig . Mounts = append ( hostConfig . Mounts , containerConfig . HostConfig . Mounts ... )
binds := hostConfig . Binds
mounts := hostConfig . Mounts
2023-05-31 05:33:39 -05:00
networkMode := hostConfig . NetworkMode
2022-10-12 11:30:56 -05:00
err = mergo . Merge ( hostConfig , containerConfig . HostConfig , mergo . WithOverride )
if err != nil {
return nil , nil , fmt . Errorf ( "Cannot merge container.HostConfig options: '%s': '%w'" , input . Options , err )
}
2022-12-06 09:58:47 -06:00
hostConfig . Binds = binds
hostConfig . Mounts = mounts
2023-05-31 05:33:39 -05:00
if len ( copts . netMode . Value ( ) ) > 0 {
logger . Warn ( "--network and --net in the options will be ignored." )
}
hostConfig . NetworkMode = networkMode
2022-10-12 11:30:56 -05:00
logger . Debugf ( "Merged container.HostConfig ==> %+v" , hostConfig )
return config , hostConfig , nil
}
2021-06-04 11:06:59 -05:00
func ( cr * containerReference ) create ( capAdd [ ] string , capDrop [ ] string ) common . Executor {
2020-02-07 00:17:58 -06:00
return func ( ctx context . Context ) error {
if cr . id != "" {
return nil
}
logger := common . Logger ( ctx )
2021-01-12 00:39:43 -06:00
isTerminal := term . IsTerminal ( int ( os . Stdout . Fd ( ) ) )
2020-02-07 00:17:58 -06:00
input := cr . input
2021-09-26 11:37:53 -05:00
2020-02-07 00:17:58 -06:00
config := & container . Config {
Add support for service containers (#1949)
* Support services (#42)
Removed createSimpleContainerName and AutoRemove flag
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services options (#45)
Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support intepolation for `env` of `services` (#47)
Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services `credentials` (#51)
If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: https://gitea.com/gitea/act/src/commit/0c1f2edb996a87ee17dcf3cfa7259c04be02abd7/pkg/runner/run_context.go#L228-L269
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Add ContainerMaxLifetime and ContainerNetworkMode options
from: https://gitea.com/gitea/act/commit/b9c20dcaa43899cb3bb327619d447248303170e0
* Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
* Check volumes (#60)
This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.
Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)
In addition, volumes specified by `options` will also be checked.
Currently, the following default volumes (see https://gitea.com/gitea/act/src/commit/a72822b3f83d3e68ffc697101b713b7badf57e2f/pkg/runner/run_context.go#L116-L166) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Remove ContainerMaxLifetime; fix lint
* Remove unused ValidVolumes
* Remove ConnectToNetwork
* Add docker stubs
* Close docker clients to prevent file descriptor leaks
* Fix the error when removing network in self-hosted mode (#69)
Fixes https://gitea.com/gitea/act_runner/issues/255
Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Move service container and network cleanup to rc.cleanUpJobContainer
* Add --network flag; default to host if not using service containers or set explicitly
* Correctly close executor to prevent fd leak
* Revert to tail instead of full path
* fix network duplication
* backport networkingConfig for aliaes
* don't hardcode netMode host
* Convert services test to table driven tests
* Add failing tests for services
* Expose service container ports onto the host
* Set container network mode in artifacts server test to host mode
* Log container network mode when creating/starting a container
* fix: Correctly handle ContainerNetworkMode
* fix: missing service container network
* Always remove service containers
Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.
* Remove networks only if no active endpoints exist
* Ensure job containers are stopped before starting a new job
* fix: go build -tags WITHOUT_DOCKER
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 04:24:52 -05:00
Image : input . Image ,
WorkingDir : input . WorkingDir ,
Env : input . Env ,
ExposedPorts : input . ExposedPorts ,
Tty : isTerminal ,
2020-02-07 00:17:58 -06:00
}
2022-10-06 17:09:43 -05:00
logger . Debugf ( "Common container.Config ==> %+v" , config )
2019-01-17 02:45:37 -06:00
2022-03-29 13:00:52 -05:00
if len ( input . Cmd ) != 0 {
config . Cmd = input . Cmd
}
if len ( input . Entrypoint ) != 0 {
config . Entrypoint = input . Entrypoint
}
2020-02-23 17:01:25 -06:00
mounts := make ( [ ] mount . Mount , 0 )
for mountSource , mountTarget := range input . Mounts {
mounts = append ( mounts , mount . Mount {
Type : mount . TypeVolume ,
Source : mountSource ,
Target : mountTarget ,
} )
2019-01-17 02:45:37 -06:00
}
2021-03-30 12:10:42 -05:00
var platSpecs * specs . Platform
2021-12-22 11:29:43 -06:00
if supportsContainerImagePlatform ( ctx , cr . cli ) && cr . input . Platform != "" {
2021-03-30 12:10:42 -05:00
desiredPlatform := strings . SplitN ( cr . input . Platform , ` / ` , 2 )
2021-03-28 23:08:40 -05:00
2021-03-30 12:10:42 -05:00
if len ( desiredPlatform ) != 2 {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "incorrect container platform option '%s'" , cr . input . Platform )
2021-03-30 12:10:42 -05:00
}
2021-03-28 23:08:40 -05:00
2021-03-30 12:10:42 -05:00
platSpecs = & specs . Platform {
Architecture : desiredPlatform [ 1 ] ,
OS : desiredPlatform [ 0 ] ,
}
}
2022-10-06 17:09:43 -05:00
hostConfig := & container . HostConfig {
Add support for service containers (#1949)
* Support services (#42)
Removed createSimpleContainerName and AutoRemove flag
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services options (#45)
Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support intepolation for `env` of `services` (#47)
Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services `credentials` (#51)
If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: https://gitea.com/gitea/act/src/commit/0c1f2edb996a87ee17dcf3cfa7259c04be02abd7/pkg/runner/run_context.go#L228-L269
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Add ContainerMaxLifetime and ContainerNetworkMode options
from: https://gitea.com/gitea/act/commit/b9c20dcaa43899cb3bb327619d447248303170e0
* Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
* Check volumes (#60)
This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.
Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)
In addition, volumes specified by `options` will also be checked.
Currently, the following default volumes (see https://gitea.com/gitea/act/src/commit/a72822b3f83d3e68ffc697101b713b7badf57e2f/pkg/runner/run_context.go#L116-L166) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Remove ContainerMaxLifetime; fix lint
* Remove unused ValidVolumes
* Remove ConnectToNetwork
* Add docker stubs
* Close docker clients to prevent file descriptor leaks
* Fix the error when removing network in self-hosted mode (#69)
Fixes https://gitea.com/gitea/act_runner/issues/255
Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Move service container and network cleanup to rc.cleanUpJobContainer
* Add --network flag; default to host if not using service containers or set explicitly
* Correctly close executor to prevent fd leak
* Revert to tail instead of full path
* fix network duplication
* backport networkingConfig for aliaes
* don't hardcode netMode host
* Convert services test to table driven tests
* Add failing tests for services
* Expose service container ports onto the host
* Set container network mode in artifacts server test to host mode
* Log container network mode when creating/starting a container
* fix: Correctly handle ContainerNetworkMode
* fix: missing service container network
* Always remove service containers
Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.
* Remove networks only if no active endpoints exist
* Ensure job containers are stopped before starting a new job
* fix: go build -tags WITHOUT_DOCKER
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 04:24:52 -05:00
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 ,
2024-02-16 22:28:10 -06:00
AutoRemove : input . AutoRemove ,
2022-10-06 17:09:43 -05:00
}
logger . Debugf ( "Common container.HostConfig ==> %+v" , hostConfig )
2022-10-12 11:30:56 -05:00
config , hostConfig , err := cr . mergeContainerConfigs ( ctx , config , hostConfig )
2020-02-07 00:17:58 -06:00
if err != nil {
2022-10-12 11:30:56 -05:00
return err
2020-02-07 00:17:58 -06:00
}
2022-10-06 17:09:43 -05:00
2023-06-05 04:21:59 -05:00
// For Gitea
config , hostConfig = cr . sanitizeConfig ( ctx , config , hostConfig )
Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
#### changes:
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-05-16 01:03:55 -05:00
// For Gitea
// network-scoped alias is supported only for containers in user defined networks
var networkingConfig * network . NetworkingConfig
Add support for service containers (#1949)
* Support services (#42)
Removed createSimpleContainerName and AutoRemove flag
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services options (#45)
Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support intepolation for `env` of `services` (#47)
Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services `credentials` (#51)
If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: https://gitea.com/gitea/act/src/commit/0c1f2edb996a87ee17dcf3cfa7259c04be02abd7/pkg/runner/run_context.go#L228-L269
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Add ContainerMaxLifetime and ContainerNetworkMode options
from: https://gitea.com/gitea/act/commit/b9c20dcaa43899cb3bb327619d447248303170e0
* Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
* Check volumes (#60)
This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.
Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)
In addition, volumes specified by `options` will also be checked.
Currently, the following default volumes (see https://gitea.com/gitea/act/src/commit/a72822b3f83d3e68ffc697101b713b7badf57e2f/pkg/runner/run_context.go#L116-L166) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Remove ContainerMaxLifetime; fix lint
* Remove unused ValidVolumes
* Remove ConnectToNetwork
* Add docker stubs
* Close docker clients to prevent file descriptor leaks
* Fix the error when removing network in self-hosted mode (#69)
Fixes https://gitea.com/gitea/act_runner/issues/255
Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Move service container and network cleanup to rc.cleanUpJobContainer
* Add --network flag; default to host if not using service containers or set explicitly
* Correctly close executor to prevent fd leak
* Revert to tail instead of full path
* fix network duplication
* backport networkingConfig for aliaes
* don't hardcode netMode host
* Convert services test to table driven tests
* Add failing tests for services
* Expose service container ports onto the host
* Set container network mode in artifacts server test to host mode
* Log container network mode when creating/starting a container
* fix: Correctly handle ContainerNetworkMode
* fix: missing service container network
* Always remove service containers
Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.
* Remove networks only if no active endpoints exist
* Ensure job containers are stopped before starting a new job
* fix: go build -tags WITHOUT_DOCKER
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 04:24:52 -05:00
logger . Debugf ( "input.NetworkAliases ==> %v" , input . NetworkAliases )
2023-11-12 12:09:25 -06:00
n := hostConfig . NetworkMode
2023-12-16 16:46:17 -06:00
// IsUserDefined and IsHost are broken on windows
if n . IsUserDefined ( ) && n != "host" && len ( input . NetworkAliases ) > 0 {
Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
#### changes:
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-05-16 01:03:55 -05:00
endpointConfig := & network . EndpointSettings {
Aliases : input . NetworkAliases ,
}
networkingConfig = & network . NetworkingConfig {
EndpointsConfig : map [ string ] * network . EndpointSettings {
input . NetworkMode : endpointConfig ,
} ,
}
}
resp , err := cr . cli . ContainerCreate ( ctx , config , hostConfig , networkingConfig , platSpecs , input . Name )
2022-10-06 17:09:43 -05:00
if err != nil {
return fmt . Errorf ( "failed to create container: '%w'" , err )
}
2021-03-28 23:08:40 -05:00
logger . Debugf ( "Created container name=%s id=%v from image %v (platform: %s)" , input . Name , resp . ID , input . Image , input . Platform )
2020-02-07 00:17:58 -06:00
logger . Debugf ( "ENV ==> %v" , input . Env )
2019-01-17 02:45:37 -06:00
2020-02-07 00:17:58 -06:00
cr . id = resp . ID
return nil
2019-01-12 22:45:25 -06:00
}
}
2021-09-27 14:01:14 -05:00
func ( cr * containerReference ) extractFromImageEnv ( env * map [ string ] string ) common . Executor {
envMap := * env
return func ( ctx context . Context ) error {
logger := common . Logger ( ctx )
inspect , _ , err := cr . cli . ImageInspectWithRaw ( ctx , cr . input . Image )
if err != nil {
logger . Error ( err )
2023-10-31 09:32:21 -05:00
return fmt . Errorf ( "inspect image: %w" , err )
}
if inspect . Config == nil {
return nil
2021-09-27 14:01:14 -05:00
}
imageEnv , err := godotenv . Unmarshal ( strings . Join ( inspect . Config . Env , "\n" ) )
if err != nil {
logger . Error ( err )
2023-10-31 09:32:21 -05:00
return fmt . Errorf ( "unmarshal image env: %w" , err )
2021-09-27 14:01:14 -05:00
}
for k , v := range imageEnv {
if k == "PATH" {
if envMap [ k ] == "" {
envMap [ k ] = v
} else {
envMap [ k ] += ` : ` + v
}
} else if envMap [ k ] == "" {
envMap [ k ] = v
}
}
env = & envMap
return nil
}
}
2021-08-10 14:40:20 -05:00
func ( cr * containerReference ) exec ( cmd [ ] string , env map [ string ] string , user , workdir string ) common . Executor {
2020-02-07 00:17:58 -06:00
return func ( ctx context . Context ) error {
logger := common . Logger ( ctx )
2021-01-12 00:41:35 -06:00
// Fix slashes when running on Windows
if runtime . GOOS == "windows" {
var newCmd [ ] string
for _ , v := range cmd {
newCmd = append ( newCmd , strings . ReplaceAll ( v , ` \ ` , ` / ` ) )
}
cmd = newCmd
}
2021-01-14 23:37:38 -06:00
2020-02-23 17:01:25 -06:00
logger . Debugf ( "Exec command '%s'" , cmd )
2021-01-12 00:39:43 -06:00
isTerminal := term . IsTerminal ( int ( os . Stdout . Fd ( ) ) )
2020-02-23 17:01:25 -06:00
envList := make ( [ ] string , 0 )
for k , v := range env {
envList = append ( envList , fmt . Sprintf ( "%s=%s" , k , v ) )
}
2021-08-10 14:40:20 -05:00
var wd string
if workdir != "" {
if strings . HasPrefix ( workdir , "/" ) {
wd = workdir
} else {
wd = fmt . Sprintf ( "%s/%s" , cr . input . WorkingDir , workdir )
}
} else {
wd = cr . input . WorkingDir
}
logger . Debugf ( "Working directory '%s'" , wd )
2020-02-23 17:01:25 -06:00
idResp , err := cr . cli . ContainerExecCreate ( ctx , cr . id , types . ExecConfig {
2021-05-24 12:09:03 -05:00
User : user ,
2020-02-23 17:01:25 -06:00
Cmd : cmd ,
2021-08-10 14:40:20 -05:00
WorkingDir : wd ,
2020-02-23 17:01:25 -06:00
Env : envList ,
Tty : isTerminal ,
AttachStderr : true ,
AttachStdout : true ,
} )
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to create exec: %w" , err )
2020-02-23 17:01:25 -06:00
}
resp , err := cr . cli . ContainerExecAttach ( ctx , idResp . ID , types . ExecStartCheck {
Tty : isTerminal ,
} )
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to attach to exec: %w" , err )
2020-02-23 17:01:25 -06:00
}
2021-05-06 08:30:12 -05:00
defer resp . Close ( )
2022-05-24 09:52:25 -05:00
err = cr . waitForCommand ( ctx , isTerminal , resp , idResp , user , workdir )
if err != nil {
return err
}
inspectResp , err := cr . cli . ContainerExecInspect ( ctx , idResp . ID )
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to inspect exec: %w" , err )
2022-05-24 09:52:25 -05:00
}
2022-06-10 16:16:42 -05:00
switch inspectResp . ExitCode {
case 0 :
2022-05-24 09:52:25 -05:00
return nil
2022-06-10 16:16:42 -05:00
case 127 :
return fmt . Errorf ( "exitcode '%d': command not found, please refer to https://github.com/nektos/act/issues/107 for more information" , inspectResp . ExitCode )
default :
return fmt . Errorf ( "exitcode '%d': failure" , inspectResp . ExitCode )
2022-05-24 09:52:25 -05:00
}
}
}
2022-06-20 17:47:39 -05:00
func ( cr * containerReference ) tryReadID ( opt string , cbk func ( id int ) ) common . Executor {
return func ( ctx context . Context ) error {
idResp , err := cr . cli . ContainerExecCreate ( ctx , cr . id , types . ExecConfig {
Cmd : [ ] string { "id" , opt } ,
AttachStdout : true ,
AttachStderr : true ,
} )
if err != nil {
return nil
}
resp , err := cr . cli . ContainerExecAttach ( ctx , idResp . ID , types . ExecStartCheck { } )
if err != nil {
return nil
}
defer resp . Close ( )
sid , err := resp . Reader . ReadString ( '\n' )
if err != nil {
return nil
}
exp := regexp . MustCompile ( ` \d+\n ` )
found := exp . FindString ( sid )
2023-03-08 08:41:25 -06:00
id , err := strconv . ParseInt ( strings . TrimSpace ( found ) , 10 , 32 )
2022-06-20 17:47:39 -05:00
if err != nil {
return nil
}
cbk ( int ( id ) )
return nil
}
}
func ( cr * containerReference ) tryReadUID ( ) common . Executor {
return cr . tryReadID ( "-u" , func ( id int ) { cr . UID = id } )
}
func ( cr * containerReference ) tryReadGID ( ) common . Executor {
return cr . tryReadID ( "-g" , func ( id int ) { cr . GID = id } )
}
2023-07-10 19:12:12 -05:00
func ( cr * containerReference ) waitForCommand ( ctx context . Context , isTerminal bool , resp types . HijackedResponse , _ types . IDResponse , _ string , _ string ) error {
2022-05-24 09:52:25 -05:00
logger := common . Logger ( ctx )
cmdResponse := make ( chan error )
go func ( ) {
2020-02-23 17:01:25 -06:00
var outWriter io . Writer
outWriter = cr . input . Stdout
if outWriter == nil {
outWriter = os . Stdout
}
errWriter := cr . input . Stderr
if errWriter == nil {
errWriter = os . Stderr
}
2022-05-24 09:52:25 -05:00
var err error
2020-02-23 17:01:25 -06:00
if ! isTerminal || os . Getenv ( "NORAW" ) != "" {
_ , err = stdcopy . StdCopy ( outWriter , errWriter , resp . Reader )
} else {
_ , err = io . Copy ( outWriter , resp . Reader )
}
2022-05-24 09:52:25 -05:00
cmdResponse <- err
} ( )
2020-02-23 17:01:25 -06:00
2022-05-24 09:52:25 -05:00
select {
case <- ctx . Done ( ) :
// send ctrl + c
_ , err := resp . Conn . Write ( [ ] byte { 3 } )
2020-02-23 17:01:25 -06:00
if err != nil {
2022-05-24 09:52:25 -05:00
logger . Warnf ( "Failed to send CTRL+C: %+s" , err )
2020-02-23 17:01:25 -06:00
}
2022-05-24 09:52:25 -05:00
// we return the context canceled error to prevent other steps
// from executing
return ctx . Err ( )
case err := <- cmdResponse :
if err != nil {
logger . Error ( err )
2020-02-23 17:01:25 -06:00
}
2022-05-24 09:52:25 -05:00
return nil
2020-02-23 17:01:25 -06:00
}
}
2023-08-08 10:18:25 -05:00
func ( cr * containerReference ) CopyTarStream ( ctx context . Context , destPath string , tarStream io . Reader ) error {
2024-01-19 17:49:35 -06:00
// Mkdir
buf := & bytes . Buffer { }
tw := tar . NewWriter ( buf )
_ = tw . WriteHeader ( & tar . Header {
Name : destPath ,
Mode : 777 ,
Typeflag : tar . TypeDir ,
} )
tw . Close ( )
err := cr . cli . CopyToContainer ( ctx , cr . id , "/" , buf , types . CopyToContainerOptions { } )
if err != nil {
return fmt . Errorf ( "failed to mkdir to copy content to container: %w" , err )
}
// Copy Content
err = cr . cli . CopyToContainer ( ctx , cr . id , destPath , tarStream , types . CopyToContainerOptions { } )
2023-08-08 10:18:25 -05:00
if err != nil {
return fmt . Errorf ( "failed to copy content to container: %w" , err )
}
2024-01-19 17:49:35 -06:00
// If this fails, then folders have wrong permissions on non root container
if cr . UID != 0 || cr . GID != 0 {
_ = cr . Exec ( [ ] string { "chown" , "-R" , fmt . Sprintf ( "%d:%d" , cr . UID , cr . GID ) , destPath } , nil , "0" , "" ) ( ctx )
}
2023-08-08 10:18:25 -05:00
return nil
}
2021-05-03 09:37:20 -05:00
func ( cr * containerReference ) copyDir ( dstPath string , srcPath string , useGitIgnore bool ) common . Executor {
2020-02-24 18:38:49 -06:00
return func ( ctx context . Context ) error {
logger := common . Logger ( ctx )
2022-10-29 12:15:38 -05:00
tarFile , err := os . CreateTemp ( "" , "act" )
2020-02-24 18:38:49 -06:00
if err != nil {
return err
}
2022-06-17 10:55:21 -05:00
logger . Debugf ( "Writing tarball %s from %s" , tarFile . Name ( ) , srcPath )
2022-04-04 10:27:00 -05:00
defer func ( tarFile * os . File ) {
name := tarFile . Name ( )
err := tarFile . Close ( )
2022-08-07 01:07:54 -05:00
if ! errors . Is ( err , os . ErrClosed ) {
2022-04-04 10:27:00 -05:00
logger . Error ( err )
}
err = os . Remove ( name )
if err != nil {
logger . Error ( err )
}
} ( tarFile )
2020-02-24 18:38:49 -06:00
tw := tar . NewWriter ( tarFile )
srcPrefix := filepath . Dir ( srcPath )
if ! strings . HasSuffix ( srcPrefix , string ( filepath . Separator ) ) {
srcPrefix += string ( filepath . Separator )
}
2022-06-17 10:55:21 -05:00
logger . Debugf ( "Stripping prefix:%s src:%s" , srcPrefix , srcPath )
2020-02-24 18:38:49 -06:00
2021-05-03 09:37:20 -05:00
var ignorer gitignore . Matcher
if useGitIgnore {
ps , err := gitignore . ReadPatterns ( polyfill . New ( osfs . New ( srcPath ) ) , nil )
if err != nil {
2022-06-17 10:55:21 -05:00
logger . Debugf ( "Error loading .gitignore: %v" , err )
2021-05-03 09:37:20 -05:00
}
2020-03-09 20:32:48 -05:00
2021-05-03 09:37:20 -05:00
ignorer = gitignore . NewMatcher ( ps )
}
2020-03-18 08:55:39 -05:00
2024-01-29 18:46:45 -06:00
fc := & filecollector . FileCollector {
Fs : & filecollector . DefaultFs { } ,
2022-04-04 10:27:00 -05:00
Ignorer : ignorer ,
SrcPath : srcPath ,
SrcPrefix : srcPrefix ,
2024-01-29 18:46:45 -06:00
Handler : & filecollector . TarCollector {
2022-04-04 10:27:00 -05:00
TarWriter : tw ,
2022-06-20 17:47:39 -05:00
UID : cr . UID ,
GID : cr . GID ,
DstDir : dstPath [ 1 : ] ,
2022-04-04 10:27:00 -05:00
} ,
}
2020-02-24 18:38:49 -06:00
2024-01-29 18:46:45 -06:00
err = filepath . Walk ( srcPath , fc . CollectFiles ( ctx , [ ] string { } ) )
2020-02-24 18:38:49 -06:00
if err != nil {
return err
}
if err := tw . Close ( ) ; err != nil {
return err
}
logger . Debugf ( "Extracting content from '%s' to '%s'" , tarFile . Name ( ) , dstPath )
_ , err = tarFile . Seek ( 0 , 0 )
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to seek tar archive: %w" , err )
2020-02-24 18:38:49 -06:00
}
2022-06-20 17:47:39 -05:00
err = cr . cli . CopyToContainer ( ctx , cr . id , "/" , tarFile , types . CopyToContainerOptions { } )
2020-02-24 18:38:49 -06:00
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to copy content to container: %w" , err )
2020-02-24 18:38:49 -06:00
}
return nil
}
}
2020-02-23 17:01:25 -06:00
func ( cr * containerReference ) copyContent ( dstPath string , files ... * FileEntry ) common . Executor {
return func ( ctx context . Context ) error {
logger := common . Logger ( ctx )
var buf bytes . Buffer
tw := tar . NewWriter ( & buf )
for _ , file := range files {
2022-06-17 10:55:21 -05:00
logger . Debugf ( "Writing entry to tarball %s len:%d" , file . Name , len ( file . Body ) )
2020-02-23 17:01:25 -06:00
hdr := & tar . Header {
Name : file . Name ,
Mode : file . Mode ,
Size : int64 ( len ( file . Body ) ) ,
2022-06-20 17:47:39 -05:00
Uid : cr . UID ,
Gid : cr . GID ,
2020-02-23 17:01:25 -06:00
}
if err := tw . WriteHeader ( hdr ) ; err != nil {
return err
2020-02-07 00:17:58 -06:00
}
2020-02-23 17:01:25 -06:00
if _ , err := tw . Write ( [ ] byte ( file . Body ) ) ; err != nil {
return err
}
}
if err := tw . Close ( ) ; err != nil {
return err
}
logger . Debugf ( "Extracting content to '%s'" , dstPath )
err := cr . cli . CopyToContainer ( ctx , cr . id , dstPath , & buf , types . CopyToContainerOptions { } )
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to copy content to container: %w" , err )
2019-01-12 22:45:25 -06:00
}
2020-02-07 00:17:58 -06:00
return nil
2019-01-12 22:45:25 -06:00
}
}
2020-02-07 00:17:58 -06:00
func ( cr * containerReference ) attach ( ) common . Executor {
return func ( ctx context . Context ) error {
out , err := cr . cli . ContainerAttach ( ctx , cr . id , types . ContainerAttachOptions {
Stream : true ,
Stdout : true ,
Stderr : true ,
} )
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to attach to container: %w" , err )
2020-02-07 00:17:58 -06:00
}
2021-01-12 00:39:43 -06:00
isTerminal := term . IsTerminal ( int ( os . Stdout . Fd ( ) ) )
2020-02-11 11:10:35 -06:00
var outWriter io . Writer
outWriter = cr . input . Stdout
if outWriter == nil {
outWriter = os . Stdout
}
errWriter := cr . input . Stderr
if errWriter == nil {
errWriter = os . Stderr
2020-02-07 00:17:58 -06:00
}
2020-02-11 11:10:35 -06:00
go func ( ) {
if ! isTerminal || os . Getenv ( "NORAW" ) != "" {
_ , err = stdcopy . StdCopy ( outWriter , errWriter , out . Reader )
} else {
_ , err = io . Copy ( outWriter , out . Reader )
}
if err != nil {
common . Logger ( ctx ) . Error ( err )
}
} ( )
2020-02-07 00:17:58 -06:00
return nil
2019-01-12 22:45:25 -06:00
}
}
2020-02-07 00:17:58 -06:00
func ( cr * containerReference ) start ( ) common . Executor {
return func ( ctx context . Context ) error {
logger := common . Logger ( ctx )
2020-02-23 17:01:25 -06:00
logger . Debugf ( "Starting container: %v" , cr . id )
2019-01-12 22:45:25 -06:00
2020-02-07 00:17:58 -06:00
if err := cr . cli . ContainerStart ( ctx , cr . id , types . ContainerStartOptions { } ) ; err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to start container: %w" , err )
2020-02-07 00:17:58 -06:00
}
2019-01-12 22:45:25 -06:00
2020-02-07 00:17:58 -06:00
logger . Debugf ( "Started container: %v" , cr . id )
return nil
}
2019-01-12 22:45:25 -06:00
}
2020-02-07 00:17:58 -06:00
func ( cr * containerReference ) wait ( ) common . Executor {
return func ( ctx context . Context ) error {
logger := common . Logger ( ctx )
statusCh , errCh := cr . cli . ContainerWait ( ctx , cr . id , container . WaitConditionNotRunning )
var statusCode int64
select {
case err := <- errCh :
if err != nil {
2022-06-10 16:16:42 -05:00
return fmt . Errorf ( "failed to wait for container: %w" , err )
2020-02-07 00:17:58 -06:00
}
case status := <- statusCh :
statusCode = status . StatusCode
2019-01-12 22:45:25 -06:00
}
2020-02-07 00:17:58 -06:00
logger . Debugf ( "Return status: %v" , statusCode )
2019-01-12 22:45:25 -06:00
2020-02-07 00:17:58 -06:00
if statusCode == 0 {
return nil
}
2019-01-12 22:45:25 -06:00
2020-02-07 00:17:58 -06:00
return fmt . Errorf ( "exit with `FAILURE`: %v" , statusCode )
}
2019-01-12 22:45:25 -06:00
}
2023-06-05 04:21:59 -05:00
2023-06-16 00:24:01 -05:00
// For Gitea
// sanitizeConfig remove the invalid configurations from `config` and `hostConfig`
2023-06-05 04:21:59 -05:00
func ( cr * containerReference ) sanitizeConfig ( ctx context . Context , config * container . Config , hostConfig * container . HostConfig ) ( * container . Config , * container . HostConfig ) {
logger := common . Logger ( ctx )
if len ( cr . input . ValidVolumes ) > 0 {
2023-06-16 00:24:01 -05:00
globs := make ( [ ] glob . Glob , 0 , len ( cr . input . ValidVolumes ) )
for _ , v := range cr . input . ValidVolumes {
if g , err := glob . Compile ( v ) ; err != nil {
logger . Errorf ( "create glob from %s error: %v" , v , err )
} else {
globs = append ( globs , g )
}
}
isValid := func ( v string ) bool {
for _ , g := range globs {
if g . Match ( v ) {
return true
}
}
return false
2023-06-05 04:21:59 -05:00
}
// sanitize binds
sanitizedBinds := make ( [ ] string , 0 , len ( hostConfig . Binds ) )
for _ , bind := range hostConfig . Binds {
parsed , err := loader . ParseVolume ( bind )
if err != nil {
logger . Warnf ( "parse volume [%s] error: %v" , bind , err )
continue
}
if parsed . Source == "" {
// anonymous volume
sanitizedBinds = append ( sanitizedBinds , bind )
continue
}
2023-06-16 00:24:01 -05:00
if isValid ( parsed . Source ) {
2023-06-05 04:21:59 -05:00
sanitizedBinds = append ( sanitizedBinds , bind )
} else {
2023-06-16 00:24:01 -05:00
logger . Warnf ( "[%s] is not a valid volume, will be ignored" , parsed . Source )
2023-06-05 04:21:59 -05:00
}
}
hostConfig . Binds = sanitizedBinds
// sanitize mounts
sanitizedMounts := make ( [ ] mount . Mount , 0 , len ( hostConfig . Mounts ) )
for _ , mt := range hostConfig . Mounts {
2023-06-16 00:24:01 -05:00
if isValid ( mt . Source ) {
2023-06-05 04:21:59 -05:00
sanitizedMounts = append ( sanitizedMounts , mt )
} else {
logger . Warnf ( "[%s] is not a valid volume, will be ignored" , mt . Source )
}
}
hostConfig . Mounts = sanitizedMounts
2023-06-16 00:24:01 -05:00
} else {
hostConfig . Binds = [ ] string { }
hostConfig . Mounts = [ ] mount . Mount { }
2023-06-05 04:21:59 -05:00
}
return config , hostConfig
}