Files
server/pipeline_executor/pipeline_executor.go
T
restitux 8e4e45047d Job exection logic now runs pipeline in container
This container's network is configured based on parameters in the config
file. If configured correctly, this will allow the pipeline script to
speak to the cursorius server over the pipeline api.
2022-12-24 22:12:30 -07:00

159 lines
4.2 KiB
Go

package pipeline_executor
import (
"bytes"
"context"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"git.ohea.xyz/cursorius/server/config"
"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 PipelineExecution struct {
Name string
Job config.Job
Ref string
}
func ExecutePipeline(pe PipelineExecution, pipelineConf config.PipelineConf) error {
jobFolder := filepath.Join(pipelineConf.WorkingDir, pe.Name)
log.Debugf("Job %v configured with URL \"%v\"", pe.Name, pe.Job.URL)
log.Debugf("Job %v configured with folder \"%v\"", pe.Name, 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", pe.Name, err)
}
log.Infof("Cloning source from URL %v", pe.Job.URL)
// TODO: should I use go-git here instead of shelling out to raw git?
cloneCmd := exec.Command("git", "clone", pe.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)
}
log.Info("Source cloned successfully")
ctx := context.Background()
imageName := "git.ohea.xyz/cursorius/cursorius:latest"
log.Infof("Pulling image %v", imageName)
pullOutput, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions{})
if err != nil {
return fmt.Errorf("could not pull image %v: %v", imageName, err)
}
buf, err := io.ReadAll(pullOutput)
if err != nil {
return fmt.Errorf("could not read from io.ReadCloser:, %v", err)
}
log.Infof("%s", buf)
err = pullOutput.Close()
if err != nil {
return fmt.Errorf("could not close io.ReadCloser: %v", err)
}
log.Info("Image pulled sucessfully")
hostConfig := container.HostConfig{}
if pipelineConf.DockerNetwork != nil {
hostConfig.NetworkMode = container.NetworkMode(*pipelineConf.DockerNetwork)
}
if pipelineConf.MountConf.Type == config.Bind {
hostConfig.Mounts = append(hostConfig.Mounts,
mount.Mount{
Type: mount.TypeBind,
Source: fmt.Sprintf("%v/%v", pipelineConf.MountConf.Source, pe.Name),
Target: "/cursorius/src",
ReadOnly: false,
Consistency: mount.ConsistencyDefault,
},
)
} else if pipelineConf.MountConf.Type == config.Volume {
hostConfig.Mounts = append(hostConfig.Mounts,
mount.Mount{
Type: mount.TypeVolume,
Source: pipelineConf.MountConf.Source,
Target: "/cursorius/src",
ReadOnly: false,
Consistency: mount.ConsistencyDefault,
},
)
}
resp, err := cli.ContainerCreate(ctx,
&container.Config{
Image: imageName,
Cmd: []string{"/launcher.sh"},
Tty: false,
Env: []string{
"CURSORIUS_SRC_DIR=/cursorius/src",
fmt.Sprintf("CUROSRIUS_SERVER_URL=%v", pipelineConf.AccessURL),
},
},
// TODO: fix running the runner in docker (add VolumesFrom to HostConfig)
&hostConfig,
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
}