package jobrunner import ( "bytes" "context" "fmt" "os" "os/exec" "path/filepath" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" "github.com/op/go-logging" ) var log = logging.MustGetLogger("cursorius-server") type Job struct { Id string URL string } func RunJob(job Job, workingDir string) error { jobFolder := filepath.Join(workingDir, job.Id) log.Debugf("Job %v configured with URL \"%v\"", job.Id, job.URL) log.Debugf("Job %v configured with folder \"%v\"", job.Id, jobFolder) err := os.RemoveAll(jobFolder) if err != nil { return fmt.Errorf("could not delete existing folder %v", jobFolder) } err = os.MkdirAll(jobFolder, 0755) if err != nil { return fmt.Errorf("could not create working directory for job %v: %v", job.Id, err) } log.Infof("Cloning source from URL %v", job.URL) cloneCmd := exec.Command("git", "clone", job.URL, jobFolder) output, err := cloneCmd.CombinedOutput() if err != nil { log.Debugf("%s", output) return fmt.Errorf("could not clone source: %v", err) } cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return fmt.Errorf("Could not create docker client: %v", err) } ctx := context.Background() resp, err := cli.ContainerCreate(ctx, &container.Config{ Image: "cursorius:latest", Cmd: []string{"/launcher.sh"}, Tty: false, Env: []string{fmt.Sprintf("CURSORIUS_SRC_DIR=/job")}, }, // TODO: fix running the runner in docker (add VolumesFrom to HostConfig) &container.HostConfig{ Mounts: []mount.Mount{{ Source: jobFolder, Target: "/job", ReadOnly: false, Consistency: mount.ConsistencyDefault, }}, }, nil, nil, "", ) if err != nil { return fmt.Errorf("could not create container: %v", err) } log.Info("Launching container") if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil { return fmt.Errorf("could not start container: %v", err) } statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning) select { case err := <-errCh: if err != nil { return fmt.Errorf("container returned error: %v", err) } case retCode := <-statusCh: log.Debugf("Container finished running with return code: %v", retCode) } out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true}) if err != nil { return fmt.Errorf("could not get container logs: %v", err) } var stdOut bytes.Buffer var stdErr bytes.Buffer stdcopy.StdCopy(&stdOut, &stdErr, out) log.Debugf("%s", stdOut.Bytes()) log.Debugf("%s", stdErr.Bytes()) return nil }