mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-30 12:20:33 +00:00
113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
|
package docker
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
|
||
|
"github.com/drone/drone/build"
|
||
|
"github.com/drone/drone/build/docker/internal"
|
||
|
"github.com/drone/drone/yaml"
|
||
|
|
||
|
"github.com/samalba/dockerclient"
|
||
|
)
|
||
|
|
||
|
type dockerEngine struct {
|
||
|
client dockerclient.Client
|
||
|
}
|
||
|
|
||
|
func (e *dockerEngine) ContainerStart(container *yaml.Container) (string, error) {
|
||
|
conf := toContainerConfig(container)
|
||
|
auth := toAuthConfig(container)
|
||
|
|
||
|
// pull the image if it does not exists or if the Container
|
||
|
// is configured to always pull a new image.
|
||
|
_, err := e.client.InspectImage(container.Image)
|
||
|
if err != nil || container.Pull {
|
||
|
e.client.PullImage(container.Image, auth)
|
||
|
}
|
||
|
|
||
|
// create and start the container and return the Container ID.
|
||
|
id, err := e.client.CreateContainer(conf, container.ID, auth)
|
||
|
if err != nil {
|
||
|
return id, err
|
||
|
}
|
||
|
err = e.client.StartContainer(id, &conf.HostConfig)
|
||
|
if err != nil {
|
||
|
|
||
|
// remove the container if it cannot be started
|
||
|
e.client.RemoveContainer(id, true, true)
|
||
|
return id, err
|
||
|
}
|
||
|
return id, nil
|
||
|
}
|
||
|
|
||
|
func (e *dockerEngine) ContainerStop(id string) error {
|
||
|
e.client.StopContainer(id, 1)
|
||
|
e.client.KillContainer(id, "9")
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (e *dockerEngine) ContainerRemove(id string) error {
|
||
|
e.client.StopContainer(id, 1)
|
||
|
e.client.KillContainer(id, "9")
|
||
|
e.client.RemoveContainer(id, true, true)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (e *dockerEngine) ContainerWait(id string) (*build.State, error) {
|
||
|
// wait for the container to exit
|
||
|
//
|
||
|
// TODO(bradrydzewski) we should have a for loop here
|
||
|
// to re-connect and wait if this channel returns a
|
||
|
// result even though the container is still running.
|
||
|
//
|
||
|
<-e.client.Wait(id)
|
||
|
v, err := e.client.InspectContainer(id)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &build.State{
|
||
|
ExitCode: v.State.ExitCode,
|
||
|
OOMKilled: v.State.OOMKilled,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (e *dockerEngine) ContainerLogs(id string) (io.ReadCloser, error) {
|
||
|
opts := &dockerclient.LogOptions{
|
||
|
Follow: true,
|
||
|
Stdout: true,
|
||
|
Stderr: true,
|
||
|
}
|
||
|
|
||
|
piper, pipew := io.Pipe()
|
||
|
go func() {
|
||
|
defer pipew.Close()
|
||
|
|
||
|
// sometimes the docker logs fails due to parsing errors. this
|
||
|
// routine will check for such a failure and attempt to resume
|
||
|
// if necessary.
|
||
|
for i := 0; i < 5; i++ {
|
||
|
if i > 0 {
|
||
|
opts.Tail = 1
|
||
|
}
|
||
|
|
||
|
rc, err := e.client.ContainerLogs(id, opts)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
defer rc.Close()
|
||
|
|
||
|
// use Docker StdCopy
|
||
|
internal.StdCopy(pipew, pipew, rc)
|
||
|
|
||
|
// check to see if the container is still running. If not,
|
||
|
// we can safely exit and assume there are no more logs left
|
||
|
// to stream.
|
||
|
v, err := e.client.InspectContainer(id)
|
||
|
if err != nil || !v.State.Running {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
return piper, nil
|
||
|
}
|