woodpecker/pipeline/backend/docker/docker.go

209 lines
5 KiB
Go
Raw Normal View History

2017-03-05 07:56:08 +00:00
package docker
import (
"context"
"io"
"os"
2017-03-05 07:56:08 +00:00
"github.com/woodpecker-ci/woodpecker/pipeline/backend"
2017-03-05 07:56:08 +00:00
2020-05-18 14:46:13 +00:00
"docker.io/go-docker"
"docker.io/go-docker/api/types"
"docker.io/go-docker/api/types/network"
"docker.io/go-docker/api/types/volume"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/term"
2017-03-05 07:56:08 +00:00
)
type engine struct {
2020-05-18 14:46:13 +00:00
client docker.APIClient
2017-03-05 07:56:08 +00:00
}
// New returns a new Docker Engine using the given client.
2020-05-18 14:46:13 +00:00
func New(cli docker.APIClient) backend.Engine {
2017-03-05 07:56:08 +00:00
return &engine{
client: cli,
}
}
// NewEnv returns a new Docker Engine using the client connection
// environment variables.
func NewEnv() (backend.Engine, error) {
2020-05-18 14:46:13 +00:00
cli, err := docker.NewEnvClient()
2017-03-05 07:56:08 +00:00
if err != nil {
return nil, err
}
return New(cli), nil
}
2018-04-01 18:34:01 +00:00
func (e *engine) Setup(_ context.Context, conf *backend.Config) error {
2017-03-05 07:56:08 +00:00
for _, vol := range conf.Volumes {
_, err := e.client.VolumeCreate(noContext, volume.VolumesCreateBody{
Name: vol.Name,
Driver: vol.Driver,
DriverOpts: vol.DriverOpts,
// Labels: defaultLabels,
})
if err != nil {
return err
}
}
for _, n := range conf.Networks {
_, err := e.client.NetworkCreate(noContext, n.Name, types.NetworkCreate{
Driver: n.Driver,
Options: n.DriverOpts,
2017-03-05 07:56:08 +00:00
// Labels: defaultLabels,
})
if err != nil {
return err
}
}
return nil
}
2018-04-01 18:34:01 +00:00
func (e *engine) Exec(ctx context.Context, proc *backend.Step) error {
2017-03-05 07:56:08 +00:00
config := toConfig(proc)
hostConfig := toHostConfig(proc)
// create pull options with encoded authorization credentials.
pullopts := types.ImagePullOptions{}
if proc.AuthConfig.Username != "" && proc.AuthConfig.Password != "" {
pullopts.RegistryAuth, _ = encodeAuthToBase64(proc.AuthConfig)
}
// automatically pull the latest version of the image if requested
// by the process configuration.
if proc.Pull {
responseBody, perr := e.client.ImagePull(ctx, config.Image, pullopts)
2017-03-05 07:56:08 +00:00
if perr == nil {
defer responseBody.Close()
2020-05-18 14:46:13 +00:00
fd, isTerminal := term.GetFdInfo(os.Stdout)
jsonmessage.DisplayJSONMessagesStream(responseBody, os.Stdout, fd, isTerminal, nil)
2017-03-05 07:56:08 +00:00
}
// fix for drone/drone#1917
if perr != nil && proc.AuthConfig.Password != "" {
return perr
}
}
_, err := e.client.ContainerCreate(ctx, config, hostConfig, nil, proc.Name)
if docker.IsErrNotFound(err) {
2017-03-05 07:56:08 +00:00
// automatically pull and try to re-create the image if the
// failure is caused because the image does not exist.
responseBody, perr := e.client.ImagePull(ctx, config.Image, pullopts)
2017-03-05 07:56:08 +00:00
if perr != nil {
return perr
}
defer responseBody.Close()
fd, isTerminal := term.GetFdInfo(os.Stdout)
jsonmessage.DisplayJSONMessagesStream(responseBody, os.Stdout, fd, isTerminal, nil)
2017-03-05 07:56:08 +00:00
_, err = e.client.ContainerCreate(ctx, config, hostConfig, nil, proc.Name)
}
if err != nil {
return err
}
if len(proc.NetworkMode) == 0 {
for _, net := range proc.Networks {
err = e.client.NetworkConnect(ctx, net.Name, proc.Name, &network.EndpointSettings{
Aliases: net.Aliases,
})
if err != nil {
return err
}
2017-03-05 07:56:08 +00:00
}
}
// if proc.Network != "host" { // or bridge, overlay, none, internal, container:<name> ....
// err = e.client.NetworkConnect(ctx, proc.Network, proc.Name, &network.EndpointSettings{
// Aliases: proc.NetworkAliases,
// })
// if err != nil {
// return err
// }
// }
return e.client.ContainerStart(ctx, proc.Name, startOpts)
}
2018-04-01 18:34:01 +00:00
func (e *engine) Kill(_ context.Context, proc *backend.Step) error {
2017-03-05 07:56:08 +00:00
return e.client.ContainerKill(noContext, proc.Name, "9")
}
2018-04-01 18:34:01 +00:00
func (e *engine) Wait(ctx context.Context, proc *backend.Step) (*backend.State, error) {
2020-05-18 14:46:13 +00:00
wait, errc := e.client.ContainerWait(ctx, proc.Name, "")
select {
case <-wait:
case <-errc:
2017-03-05 07:56:08 +00:00
}
2020-05-18 14:46:13 +00:00
info, err := e.client.ContainerInspect(ctx, proc.Name)
2017-03-05 07:56:08 +00:00
if err != nil {
return nil, err
}
if info.State.Running {
// todo
}
return &backend.State{
Exited: true,
ExitCode: info.State.ExitCode,
OOMKilled: info.State.OOMKilled,
}, nil
}
2018-04-01 18:34:01 +00:00
func (e *engine) Tail(ctx context.Context, proc *backend.Step) (io.ReadCloser, error) {
logs, err := e.client.ContainerLogs(ctx, proc.Name, logsOpts)
2017-03-05 07:56:08 +00:00
if err != nil {
return nil, err
}
rc, wc := io.Pipe()
go func() {
stdcopy.StdCopy(wc, wc, logs)
logs.Close()
wc.Close()
rc.Close()
}()
return rc, nil
}
2018-04-01 18:34:01 +00:00
func (e *engine) Destroy(_ context.Context, conf *backend.Config) error {
2017-03-05 07:56:08 +00:00
for _, stage := range conf.Stages {
for _, step := range stage.Steps {
e.client.ContainerKill(noContext, step.Name, "9")
e.client.ContainerRemove(noContext, step.Name, removeOpts)
}
}
for _, v := range conf.Volumes {
e.client.VolumeRemove(noContext, v.Name, true)
2017-03-05 07:56:08 +00:00
}
for _, n := range conf.Networks {
e.client.NetworkRemove(noContext, n.Name)
2017-03-05 07:56:08 +00:00
}
return nil
}
var (
noContext = context.Background()
startOpts = types.ContainerStartOptions{}
removeOpts = types.ContainerRemoveOptions{
RemoveVolumes: true,
RemoveLinks: false,
Force: false,
}
logsOpts = types.ContainerLogsOptions{
Follow: true,
ShowStdout: true,
ShowStderr: true,
Details: false,
Timestamps: false,
}
)