Can define volumes to collect artifacts
This commit is contained in:
parent
d2a49e5740
commit
e3a911cd05
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
|
artifacts
|
||||||
minifaas
|
minifaas
|
||||||
vendor
|
vendor
|
48
app.go
48
app.go
@ -5,8 +5,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"nhooyr.io/websocket"
|
"nhooyr.io/websocket"
|
||||||
@ -21,12 +23,20 @@ type App struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewApp() App {
|
func NewApp() App {
|
||||||
|
//Check rights on artifacts directory
|
||||||
|
os.MkdirAll("./artifacts", os.ModePerm)
|
||||||
|
|
||||||
//gin.SetMode(gin.ReleaseMode)
|
//gin.SetMode(gin.ReleaseMode)
|
||||||
gin.ForceConsoleColor()
|
gin.ForceConsoleColor()
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
ui.DeclareRoutes(router)
|
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) {
|
router.GET("/api/version", func(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{"version": 0.1})
|
c.JSON(http.StatusOK, gin.H{"version": 0.1})
|
||||||
})
|
})
|
||||||
@ -34,7 +44,7 @@ func NewApp() App {
|
|||||||
router.GET("/api/ps", func(c *gin.Context) {
|
router.GET("/api/ps", func(c *gin.Context) {
|
||||||
containers, err := docker.Ps()
|
containers, err := docker.Ps()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,25 +52,47 @@ func NewApp() App {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.GET("/api/run", func(c *gin.Context) {
|
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 {
|
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})
|
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) {
|
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)
|
ws, err := websocket.Accept(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error get connection", err)
|
log.Fatal("error get connection", err)
|
||||||
}
|
}
|
||||||
defer ws.Close(websocket.StatusInternalError, "the sky is falling")
|
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()
|
r, w := io.Pipe()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(c.Request.Context(), time.Second*10)
|
ctx, cancel := context.WithTimeout(c.Request.Context(), time.Second*10)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"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/api/types/network"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
oci "github.com/opencontainers/image-spec/specs-go/v1"
|
oci "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@ -29,7 +30,7 @@ func Ps() ([]types.Container, error) {
|
|||||||
return containers, nil
|
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()
|
cli, err := newCli()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -42,8 +43,13 @@ func Run(image string, cmd []string) (string, error) {
|
|||||||
AttachStderr: true,
|
AttachStderr: true,
|
||||||
Image: image,
|
Image: image,
|
||||||
Cmd: cmd,
|
Cmd: cmd,
|
||||||
|
Volumes: map[string]struct{}{
|
||||||
|
"/data": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&container.HostConfig{
|
||||||
|
Mounts: mounts,
|
||||||
},
|
},
|
||||||
&container.HostConfig{},
|
|
||||||
&network.NetworkingConfig{},
|
&network.NetworkingConfig{},
|
||||||
&oci.Platform{
|
&oci.Platform{
|
||||||
Architecture: "amd64",
|
Architecture: "amd64",
|
||||||
|
68
engine/docker/volumes.go
Normal file
68
engine/docker/volumes.go
Normal file
@ -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
|
||||||
|
}
|
@ -23,6 +23,14 @@
|
|||||||
it's ID is <code id="jobid"></code>
|
it's ID is <code id="jobid"></code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="volumes-card" class="mb-3 card" style="display: none;">
|
||||||
|
<h5 class="card-header">
|
||||||
|
Volumes
|
||||||
|
</h5>
|
||||||
|
<div id="volumes" class="list-group">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="logs-card" class="mb-3 card" style="display: none;">
|
<div id="logs-card" class="mb-3 card" style="display: none;">
|
||||||
<h5 class="card-header">
|
<h5 class="card-header">
|
||||||
Here is your container logs output:
|
Here is your container logs output:
|
||||||
|
@ -46,6 +46,8 @@ function fetchLogs() {
|
|||||||
ws.onclose = function (evt) {
|
ws.onclose = function (evt) {
|
||||||
console.log("Connection closed.");
|
console.log("Connection closed.");
|
||||||
|
|
||||||
|
fetchVolumes()
|
||||||
|
|
||||||
current_job = null;
|
current_job = null;
|
||||||
|
|
||||||
document.getElementById("btnlaunch").disabled = false;
|
document.getElementById("btnlaunch").disabled = false;
|
||||||
@ -53,3 +55,25 @@ function fetchLogs() {
|
|||||||
document.getElementById("started-alert").style.display = "none";
|
document.getElementById("started-alert").style.display = "none";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchVolumes() {
|
||||||
|
fetch('/api/jobs/' + current_job + '/volumes')
|
||||||
|
.then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(data) {
|
||||||
|
document.getElementById("volumes").innerHTML = "";
|
||||||
|
|
||||||
|
for (const i in data) {
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.className = "list-group-item list-group-item-action";
|
||||||
|
a.href = "/artifacts" + data[i];
|
||||||
|
a.innerText = data[i];
|
||||||
|
document.getElementById("volumes").appendChild(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.length > 0) {
|
||||||
|
document.getElementById("volumes-card").style.display = "block";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user