// removeContainers removes all containers parallelly. // It retuns error channel and if something wrong, error is sent there. func removeContainers(client dockerclient.Client, containers []dockerclient.Container, force, delVolume bool) (chan dockerclient.Container, chan error) { var wg sync.WaitGroup resultCh, errCh := make(chan dockerclient.Container), make(chan error) for _, container := range containers { wg.Add(1) go func(c dockerclient.Container) { defer wg.Done() if err := client.RemoveContainer(c.Id, force, delVolume); err != nil { errCh <- fmt.Errorf( "failed to remove %s (%s): %s", c.Names[0], c.Id, err) return } resultCh <- c }(container) } go func() { // Wait until all remove task and close error channnel then wg.Wait() close(resultCh) close(errCh) }() return resultCh, errCh }
func run(client dockerclient.Client, args []string, input string) (int, error) { image := "drone/drone-exec:latest" entrypoint := []string{"/bin/drone-exec"} args = append(args, "--", input) conf := &dockerclient.ContainerConfig{ Image: image, Entrypoint: entrypoint, Cmd: args, HostConfig: dockerclient.HostConfig{ Binds: []string{"/var/run/docker.sock:/var/run/docker.sock"}, }, Volumes: map[string]struct{}{ "/var/run/docker.sock": struct{}{}, }, } info, err := docker.Run(client, conf, false) if err != nil { return 0, err } client.StopContainer(info.Id, 15) client.RemoveContainer(info.Id, true, true) return info.State.ExitCode, err }
func (e *engine) runJobNotify(r *Task, client dockerclient.Client) error { name := fmt.Sprintf("drone_build_%d_notify", r.Build.ID) defer func() { client.KillContainer(name, "9") client.RemoveContainer(name, true, true) }() // encode the build payload to write to stdin // when launching the build container in, err := encodeToLegacyFormat(r) if err != nil { log.Errorf("failure to marshal work. %s", err) return err } args := DefaultNotifyArgs args = append(args, "--") args = append(args, string(in)) conf := &dockerclient.ContainerConfig{ Image: DefaultAgent, Entrypoint: DefaultEntrypoint, Cmd: args, Env: e.envs, HostConfig: dockerclient.HostConfig{ Binds: []string{"/var/run/docker.sock:/var/run/docker.sock"}, MemorySwappiness: -1, }, Volumes: map[string]struct{}{ "/var/run/docker.sock": struct{}{}, }, } log.Infof("preparing container %s", name) info, err := docker.Run(client, conf, name) if err != nil { log.Errorf("Error starting notification container %s. %s", name, err) } // for debugging purposes we print a failed notification executions // output to the logs. Otherwise we have no way to troubleshoot failed // notifications. This is temporary code until I've come up with // a better solution. if info != nil && info.State.ExitCode != 0 && log.GetLevel() >= log.InfoLevel { var buf bytes.Buffer rc, err := client.ContainerLogs(name, docker.LogOpts) if err == nil { defer rc.Close() stdcopy.StdCopy(&buf, &buf, io.LimitReader(rc, 50000)) } log.Infof("Notification container %s exited with %d", name, info.State.ExitCode) log.Infoln(buf.String()) } return err }
func Start(client dockerclient.Client, conf *dockerclient.ContainerConfig, auth *dockerclient.AuthConfig, pull bool) (*dockerclient.ContainerInfo, error) { // force-pull the image if specified. if pull { log.Printf("Pulling image %s", conf.Image) client.PullImage(conf.Image, auth) } // attempts to create the contianer id, err := client.CreateContainer(conf, "", auth) if err != nil { log.Printf("Pulling image %s", conf.Image) // and pull the image and re-create if that fails err = client.PullImage(conf.Image, auth) if err != nil { log.Errorf("Error pulling %s. %s\n", conf.Image, err) return nil, err } id, err = client.CreateContainer(conf, "", auth) if err != nil { log.Errorf("Error creating %s. %s\n", conf.Image, err) client.RemoveContainer(id, true, true) return nil, err } } // fetches the container information info, err := client.InspectContainer(id) if err != nil { log.Errorf("Error inspecting %s. %s\n", conf.Image, err) client.RemoveContainer(id, true, true) return nil, err } // starts the container err = client.StartContainer(id, &conf.HostConfig) if err != nil { log.Errorf("Error starting %s. %s\n", conf.Image, err) } return info, err }
// RunDaemon creates the docker container, pulling images if necessary, starts // the container and returns the container information. It does not wait for // the container to exit. func RunDaemon(client dockerclient.Client, conf *dockerclient.ContainerConfig, name string) (*dockerclient.ContainerInfo, error) { // attempts to create the contianer id, err := client.CreateContainer(conf, name) if err != nil { // and pull the image and re-create if that fails err = client.PullImage(conf.Image, nil) if err != nil { return nil, err } id, err = client.CreateContainer(conf, name) if err != nil { client.RemoveContainer(id, true, true) return nil, err } } // fetches the container information info, err := client.InspectContainer(id) if err != nil { client.RemoveContainer(id, true, true) return nil, err } // starts the container err = client.StartContainer(id, &conf.HostConfig) if err != nil { client.RemoveContainer(id, true, true) return nil, err } return info, err }
func (e *engine) runJob(c context.Context, r *Task, updater *updater, client dockerclient.Client) error { name := fmt.Sprintf("drone_build_%d_job_%d", r.Build.ID, r.Job.ID) defer func() { if r.Job.Status == model.StatusRunning { r.Job.Status = model.StatusError r.Job.Finished = time.Now().UTC().Unix() r.Job.ExitCode = 255 } if r.Job.Status == model.StatusPending { r.Job.Status = model.StatusError r.Job.Started = time.Now().UTC().Unix() r.Job.Finished = time.Now().UTC().Unix() r.Job.ExitCode = 255 } updater.SetJob(c, r) client.KillContainer(name, "9") client.RemoveContainer(name, true, true) }() // marks the task as running r.Job.Status = model.StatusRunning r.Job.Started = time.Now().UTC().Unix() // encode the build payload to write to stdin // when launching the build container in, err := encodeToLegacyFormat(r) if err != nil { log.Errorf("failure to marshal work. %s", err) return err } // CREATE AND START BUILD args := DefaultBuildArgs if r.Build.Event == model.EventPull { args = DefaultPullRequestArgs } args = append(args, "--") args = append(args, string(in)) conf := &dockerclient.ContainerConfig{ Image: DefaultAgent, Entrypoint: DefaultEntrypoint, Cmd: args, Env: e.envs, HostConfig: dockerclient.HostConfig{ Binds: []string{"/var/run/docker.sock:/var/run/docker.sock"}, }, Volumes: map[string]struct{}{ "/var/run/docker.sock": struct{}{}, }, } log.Infof("preparing container %s", name) client.PullImage(conf.Image, nil) _, err = docker.RunDaemon(client, conf, name) if err != nil { log.Errorf("error starting build container. %s", err) return err } // UPDATE STATUS err = updater.SetJob(c, r) if err != nil { log.Errorf("error updating job status as running. %s", err) return err } // WAIT FOR OUTPUT info, builderr := docker.Wait(client, name) switch { case info.State.ExitCode == 128: r.Job.ExitCode = info.State.ExitCode r.Job.Status = model.StatusKilled case info.State.ExitCode == 130: r.Job.ExitCode = info.State.ExitCode r.Job.Status = model.StatusKilled case builderr != nil: r.Job.Status = model.StatusError case info.State.ExitCode != 0: r.Job.ExitCode = info.State.ExitCode r.Job.Status = model.StatusFailure default: r.Job.Status = model.StatusSuccess } // send the logs to the datastore var buf bytes.Buffer rc, err := client.ContainerLogs(name, docker.LogOpts) if err != nil && builderr != nil { buf.WriteString("Error launching build") buf.WriteString(builderr.Error()) } else if err != nil { buf.WriteString("Error launching build") buf.WriteString(err.Error()) log.Errorf("error opening connection to logs. %s", err) return err } else { defer rc.Close() stdcopy.StdCopy(&buf, &buf, io.LimitReader(rc, 5000000)) } // update the task in the datastore r.Job.Finished = time.Now().UTC().Unix() err = updater.SetJob(c, r) if err != nil { log.Errorf("error updating job after completion. %s", err) return err } err = updater.SetLogs(c, r, ioutil.NopCloser(&buf)) if err != nil { log.Errorf("error updating logs. %s", err) return err } log.Debugf("completed job %d with status %s.", r.Job.ID, r.Job.Status) return nil }