diff --git a/.gitignore b/.gitignore
index 0d5de8b..7e0998a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
+artifacts
minifaas
vendor
\ No newline at end of file
diff --git a/app.go b/app.go
index a89270e..b84b1ba 100644
--- a/app.go
+++ b/app.go
@@ -5,8 +5,10 @@ import (
"io"
"log"
"net/http"
+ "os"
"time"
+ "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/pkg/stdcopy"
"github.com/gin-gonic/gin"
"nhooyr.io/websocket"
@@ -21,12 +23,20 @@ type App struct {
}
func NewApp() App {
+ //Check rights on artifacts directory
+ os.MkdirAll("./artifacts", os.ModePerm)
+
//gin.SetMode(gin.ReleaseMode)
gin.ForceConsoleColor()
router := gin.Default()
ui.DeclareRoutes(router)
+ artifacts := http.Dir("./artifacts")
+ router.GET("/artifacts/*path", func(c *gin.Context) {
+ c.FileFromFS(c.Param("path"), artifacts)
+ })
+
router.GET("/api/version", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"version": 0.1})
})
@@ -34,7 +44,7 @@ func NewApp() App {
router.GET("/api/ps", func(c *gin.Context) {
containers, err := docker.Ps()
if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
@@ -42,25 +52,47 @@ func NewApp() App {
})
router.GET("/api/run", func(c *gin.Context) {
- ctrid, err := docker.Run("alpine", []string{"sh", "-c", "for i in `seq 20`; do echo $i; sleep 0.5; done"})
+ myVolume, err := docker.CreateVolumeDir("/data", false)
if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": err})
+ c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ return
+ }
+
+ ctrid, err := docker.Run(
+ "alpine",
+ []string{"sh", "-c", "touch /data/work_done; for i in `seq 20`; do echo $i; sleep 0.5; done"},
+ []mount.Mount{*myVolume},
+ )
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ return
}
c.JSON(http.StatusOK, gin.H{"jobid": ctrid})
})
+ router.GET("/api/jobs/:jobid/volumes", func(c *gin.Context) {
+ volumes, err := docker.GetArtifactsVolumes(c.Param("jobid"))
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ c.JSON(http.StatusOK, volumes)
+ })
+
router.GET("/api/jobs/:jobid/logs", func(c *gin.Context) {
+ stream, err := docker.Logs(c.Param("jobid"))
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
ws, err := websocket.Accept(c.Writer, c.Request, nil)
if err != nil {
log.Fatal("error get connection", err)
}
defer ws.Close(websocket.StatusInternalError, "the sky is falling")
- stream, err := docker.Logs(c.Param("jobid"))
- if err != nil {
- log.Fatal("Unable to get log stream:", err)
- }
-
r, w := io.Pipe()
ctx, cancel := context.WithTimeout(c.Request.Context(), time.Second*10)
diff --git a/engine/docker/docker.go b/engine/docker/docker.go
index d28626e..fb7db08 100644
--- a/engine/docker/docker.go
+++ b/engine/docker/docker.go
@@ -6,6 +6,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
+ "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
oci "github.com/opencontainers/image-spec/specs-go/v1"
@@ -29,7 +30,7 @@ func Ps() ([]types.Container, error) {
return containers, nil
}
-func Run(image string, cmd []string) (string, error) {
+func Run(image string, cmd []string, mounts []mount.Mount) (string, error) {
cli, err := newCli()
if err != nil {
return "", err
@@ -42,8 +43,13 @@ func Run(image string, cmd []string) (string, error) {
AttachStderr: true,
Image: image,
Cmd: cmd,
+ Volumes: map[string]struct{}{
+ "/data": {},
+ },
+ },
+ &container.HostConfig{
+ Mounts: mounts,
},
- &container.HostConfig{},
&network.NetworkingConfig{},
&oci.Platform{
Architecture: "amd64",
diff --git a/engine/docker/volumes.go b/engine/docker/volumes.go
new file mode 100644
index 0000000..2aa9fff
--- /dev/null
+++ b/engine/docker/volumes.go
@@ -0,0 +1,68 @@
+package docker
+
+import (
+ "context"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/mount"
+)
+
+func CreateVolumeDir(target string, readOnly bool) (*mount.Mount, error) {
+ abs, err := filepath.Abs("artifacts")
+ if err != nil {
+ return nil, err
+ }
+
+ dir, err := ioutil.TempDir(abs, "")
+ if err != nil {
+ return nil, err
+ }
+
+ return &mount.Mount{
+ Type: mount.TypeBind,
+ Source: dir,
+ Target: target,
+ ReadOnly: readOnly,
+ }, nil
+}
+
+func GetArtifactsVolumes(id string) (ret []string, err error) {
+ abs, err := filepath.Abs("artifacts")
+ if err != nil {
+ return nil, err
+ }
+
+ mnt, err := GetVolumes(id)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, m := range mnt {
+ if m.Type == mount.TypeBind && strings.HasPrefix(m.Source, abs) {
+ ret = append(ret, strings.TrimPrefix(m.Source, abs))
+ }
+ }
+
+ return
+}
+
+func GetVolumes(id string) (ret []types.MountPoint, err error) {
+ cli, err := newCli()
+ if err != nil {
+ return nil, err
+ }
+
+ ctr, err := cli.ContainerInspect(context.Background(), id)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, mnt := range ctr.Mounts {
+ ret = append(ret, mnt)
+ }
+
+ return
+}
diff --git a/ui/index.html b/ui/index.html
index edad158..106ad6a 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -23,6 +23,14 @@
it's ID is
+