mirror of
https://github.com/cuigh/swirl
synced 2025-03-03 10:41:31 +00:00
Add container logs page
This commit is contained in:
parent
5693f1426e
commit
400a891b29
@ -1154,6 +1154,86 @@ var Swirl;
|
||||
})(Config = Swirl.Config || (Swirl.Config = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Container;
|
||||
(function (Container) {
|
||||
var Modal = Swirl.Core.Modal;
|
||||
var Table = Swirl.Core.ListTable;
|
||||
class ListPage {
|
||||
constructor() {
|
||||
this.table = new Table("#table-items");
|
||||
this.table.on("delete-container", this.deleteContainer.bind(this));
|
||||
$("#btn-delete").click(this.deleteContainers.bind(this));
|
||||
}
|
||||
deleteContainer(e) {
|
||||
let $tr = $(e.target).closest("tr");
|
||||
let name = $tr.find("td:eq(1)").text().trim();
|
||||
let id = $tr.find(":checkbox:first").val();
|
||||
Modal.confirm(`Are you sure to remove container: <strong>${name}</strong>?`, "Delete container", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: id }).trigger(e.target).encoder("form").json(() => {
|
||||
$tr.remove();
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
deleteContainers() {
|
||||
let ids = this.table.selectedKeys();
|
||||
if (ids.length == 0) {
|
||||
Modal.alert("Please select one or more items.");
|
||||
return;
|
||||
}
|
||||
Modal.confirm(`Are you sure to remove ${ids.length} containers?`, "Delete containers", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: ids.join(",") }).trigger(e.target).encoder("form").json(() => {
|
||||
this.table.selectedRows().remove();
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Container.ListPage = ListPage;
|
||||
})(Container = Swirl.Container || (Swirl.Container = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Image;
|
||||
(function (Image) {
|
||||
var Modal = Swirl.Core.Modal;
|
||||
var Table = Swirl.Core.ListTable;
|
||||
class ListPage {
|
||||
constructor() {
|
||||
this.table = new Table("#table-items");
|
||||
this.table.on("delete-image", this.deleteImage.bind(this));
|
||||
$("#btn-delete").click(this.deleteImages.bind(this));
|
||||
}
|
||||
deleteImage(e) {
|
||||
let $tr = $(e.target).closest("tr");
|
||||
let name = $tr.find("td:eq(1)").text().trim();
|
||||
let id = $tr.find(":checkbox:first").val();
|
||||
Modal.confirm(`Are you sure to remove image: <strong>${name}</strong>?`, "Delete image", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: id }).trigger(e.target).encoder("form").json(() => {
|
||||
$tr.remove();
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
deleteImages() {
|
||||
let ids = this.table.selectedKeys();
|
||||
if (ids.length == 0) {
|
||||
Modal.alert("Please select one or more items.");
|
||||
return;
|
||||
}
|
||||
Modal.confirm(`Are you sure to remove ${ids.length} images?`, "Delete images", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: ids.join(",") }).trigger(e.target).encoder("form").json(() => {
|
||||
this.table.selectedRows().remove();
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Image.ListPage = ListPage;
|
||||
})(Image = Swirl.Image || (Swirl.Image = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Network;
|
||||
(function (Network) {
|
||||
@ -1944,44 +2024,4 @@ var Swirl;
|
||||
Volume.NewPage = NewPage;
|
||||
})(Volume = Swirl.Volume || (Swirl.Volume = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Image;
|
||||
(function (Image) {
|
||||
var Modal = Swirl.Core.Modal;
|
||||
var Table = Swirl.Core.ListTable;
|
||||
class ListPage {
|
||||
constructor() {
|
||||
this.table = new Table("#table-items");
|
||||
this.table.on("delete-image", this.deleteImage.bind(this));
|
||||
$("#btn-delete").click(this.deleteImages.bind(this));
|
||||
}
|
||||
deleteImage(e) {
|
||||
let $tr = $(e.target).closest("tr");
|
||||
let name = $tr.find("td:eq(1)").text().trim();
|
||||
let id = $tr.find(":checkbox:first").val();
|
||||
Modal.confirm(`Are you sure to remove image: <strong>${name}</strong>?`, "Delete image", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: id }).trigger(e.target).encoder("form").json(() => {
|
||||
$tr.remove();
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
deleteImages() {
|
||||
let ids = this.table.selectedKeys();
|
||||
if (ids.length == 0) {
|
||||
Modal.alert("Please select one or more items.");
|
||||
return;
|
||||
}
|
||||
Modal.confirm(`Are you sure to remove ${ids.length} images?`, "Delete images", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: ids.join(",") }).trigger(e.target).encoder("form").json(() => {
|
||||
this.table.selectedRows().remove();
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Image.ListPage = ListPage;
|
||||
})(Image = Swirl.Image || (Swirl.Image = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
//# sourceMappingURL=swirl.js.map
|
File diff suppressed because one or more lines are too long
45
assets/swirl/ts/container/list.ts
Normal file
45
assets/swirl/ts/container/list.ts
Normal file
@ -0,0 +1,45 @@
|
||||
///<reference path="../core/core.ts" />
|
||||
namespace Swirl.Container {
|
||||
import Modal = Swirl.Core.Modal;
|
||||
import AjaxResult = Swirl.Core.AjaxResult;
|
||||
import Table = Swirl.Core.ListTable;
|
||||
|
||||
export class ListPage {
|
||||
private table: Table;
|
||||
|
||||
constructor() {
|
||||
this.table = new Table("#table-items");
|
||||
|
||||
// bind events
|
||||
this.table.on("delete-container", this.deleteContainer.bind(this));
|
||||
$("#btn-delete").click(this.deleteContainers.bind(this));
|
||||
}
|
||||
|
||||
private deleteContainer(e: JQueryEventObject) {
|
||||
let $tr = $(e.target).closest("tr");
|
||||
let name = $tr.find("td:eq(1)").text().trim();
|
||||
let id = $tr.find(":checkbox:first").val();
|
||||
Modal.confirm(`Are you sure to remove container: <strong>${name}</strong>?`, "Delete container", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: id }).trigger(e.target).encoder("form").json<AjaxResult>(() => {
|
||||
$tr.remove();
|
||||
dlg.close();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private deleteContainers() {
|
||||
let ids = this.table.selectedKeys();
|
||||
if (ids.length == 0) {
|
||||
Modal.alert("Please select one or more items.");
|
||||
return;
|
||||
}
|
||||
|
||||
Modal.confirm(`Are you sure to remove ${ids.length} containers?`, "Delete containers", (dlg, e) => {
|
||||
$ajax.post("delete", { ids: ids.join(",") }).trigger(e.target).encoder("form").json<AjaxResult>(() => {
|
||||
this.table.selectedRows().remove();
|
||||
dlg.close();
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +1,44 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/cuigh/swirl/misc"
|
||||
"github.com/cuigh/swirl/model"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
// ContainerList return containers on the host.
|
||||
func ContainerList(name string, pageIndex, pageSize int) (infos []*model.ContainerListInfo, totalCount int, err error) {
|
||||
func ContainerList(args *model.ContainerListArgs) (infos []*model.ContainerListInfo, totalCount int, err error) {
|
||||
err = mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
|
||||
var (
|
||||
containers []types.Container
|
||||
opts = types.ContainerListOptions{}
|
||||
opts = types.ContainerListOptions{Filters: filters.NewArgs()}
|
||||
)
|
||||
|
||||
if name != "" {
|
||||
opts.Filters = filters.NewArgs()
|
||||
opts.Filters.Add("name", name)
|
||||
if args.Filter == "" {
|
||||
opts.All = true
|
||||
} else {
|
||||
opts.Filters.Add("status", args.Filter)
|
||||
}
|
||||
if args.Name != "" {
|
||||
opts.Filters.Add("name", args.Name)
|
||||
}
|
||||
|
||||
containers, err = cli.ContainerList(ctx, opts)
|
||||
if err == nil {
|
||||
//sort.Slice(containers, func(i, j int) bool {
|
||||
// return containers[i] < containers[j].Description.Hostname
|
||||
//})
|
||||
totalCount = len(containers)
|
||||
start, end := misc.Page(totalCount, pageIndex, pageSize)
|
||||
start, end := misc.Page(totalCount, args.PageIndex, args.PageSize)
|
||||
containers = containers[start:end]
|
||||
if length := len(containers); length > 0 {
|
||||
infos = make([]*model.ContainerListInfo, length)
|
||||
@ -62,3 +72,42 @@ func ContainerInspectRaw(id string) (container types.ContainerJSON, raw []byte,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ContainerRemove remove a container.
|
||||
func ContainerRemove(id string) error {
|
||||
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
|
||||
opts := types.ContainerRemoveOptions{}
|
||||
err = cli.ContainerRemove(ctx, id, opts)
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// ContainerLogs returns the logs generated by a container.
|
||||
func ContainerLogs(id string, line int, timestamps bool) (stdout, stderr *bytes.Buffer, err error) {
|
||||
var (
|
||||
ctx context.Context
|
||||
cli *client.Client
|
||||
rc io.ReadCloser
|
||||
)
|
||||
|
||||
ctx, cli, err = mgr.Client()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
opts := types.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Tail: strconv.Itoa(line),
|
||||
Timestamps: timestamps,
|
||||
//Since: (time.Hour * 24).String()
|
||||
}
|
||||
if rc, err = cli.ContainerLogs(ctx, id, opts); err == nil {
|
||||
defer rc.Close()
|
||||
|
||||
stdout = &bytes.Buffer{}
|
||||
stderr = &bytes.Buffer{}
|
||||
_, err = stdcopy.StdCopy(stdout, stderr, rc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cuigh/auxo/net/web"
|
||||
"github.com/cuigh/auxo/util/cast"
|
||||
"github.com/cuigh/swirl/biz/docker"
|
||||
@ -12,51 +14,95 @@ type ContainerController struct {
|
||||
List web.HandlerFunc `path:"/" name:"container.list" authorize:"!" desc:"container list page"`
|
||||
Detail web.HandlerFunc `path:"/:id/detail" name:"container.detail" authorize:"!" desc:"container detail page"`
|
||||
Raw web.HandlerFunc `path:"/:id/raw" name:"container.raw" authorize:"!" desc:"container raw page"`
|
||||
Logs web.HandlerFunc `path:"/:id/logs" name:"container.logs" authorize:"!" desc:"container logs page"`
|
||||
Delete web.HandlerFunc `path:"/delete" method:"post" name:"container.delete" authorize:"!" desc:"delete container"`
|
||||
}
|
||||
|
||||
func Container() (c *ContainerController) {
|
||||
c = &ContainerController{}
|
||||
|
||||
c.List = func(ctx web.Context) error {
|
||||
name := ctx.Q("name")
|
||||
page := cast.ToIntD(ctx.Q("page"), 1)
|
||||
containers, totalCount, err := docker.ContainerList(name, page, model.PageSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newPagerModel(ctx, totalCount, model.PageSize, page).
|
||||
Add("Name", name).
|
||||
Add("Containers", containers)
|
||||
return ctx.Render("container/list", m)
|
||||
return &ContainerController{
|
||||
List: containerList,
|
||||
Detail: containerDetail,
|
||||
Raw: containerRaw,
|
||||
Logs: containerLogs,
|
||||
Delete: containerDelete,
|
||||
}
|
||||
|
||||
c.Detail = func(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
container, err := docker.ContainerInspect(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Add("Container", container)
|
||||
return ctx.Render("container/detail", m)
|
||||
}
|
||||
|
||||
c.Raw = func(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
container, raw, err := docker.ContainerInspectRaw(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
j, err := misc.JSONIndent(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Add("Container", container).Add("Raw", j)
|
||||
return ctx.Render("container/raw", m)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func containerList(ctx web.Context) error {
|
||||
args := &model.ContainerListArgs{}
|
||||
err := ctx.Bind(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.PageSize = model.PageSize
|
||||
if args.PageIndex == 0 {
|
||||
args.PageIndex = 1
|
||||
}
|
||||
|
||||
containers, totalCount, err := docker.ContainerList(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newPagerModel(ctx, totalCount, model.PageSize, args.PageIndex).
|
||||
Add("Name", args.Name).
|
||||
Add("Filter", args.Filter).
|
||||
Add("Containers", containers)
|
||||
return ctx.Render("container/list", m)
|
||||
}
|
||||
|
||||
func containerDetail(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
container, err := docker.ContainerInspect(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Add("Container", container)
|
||||
return ctx.Render("container/detail", m)
|
||||
}
|
||||
|
||||
func containerRaw(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
container, raw, err := docker.ContainerInspectRaw(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
j, err := misc.JSONIndent(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Add("Container", container).Add("Raw", j)
|
||||
return ctx.Render("container/raw", m)
|
||||
}
|
||||
|
||||
func containerLogs(ctx web.Context) error {
|
||||
id := ctx.P("id")
|
||||
container, _, err := docker.ContainerInspectRaw(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
line := cast.ToIntD(ctx.Q("line"), 500)
|
||||
timestamps := cast.ToBoolD(ctx.Q("timestamps"), false)
|
||||
stdout, stderr, err := docker.ContainerLogs(id, line, timestamps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := newModel(ctx).Add("Container", container).Add("Line", line).Add("Timestamps", timestamps).
|
||||
Add("Stdout", stdout.String()).Add("Stderr", stderr.String())
|
||||
return ctx.Render("container/logs", m)
|
||||
}
|
||||
|
||||
func containerDelete(ctx web.Context) error {
|
||||
ids := strings.Split(ctx.F("ids"), ",")
|
||||
for _, id := range ids {
|
||||
if err := docker.ContainerRemove(id); err != nil {
|
||||
return ajaxResult(ctx, err)
|
||||
}
|
||||
}
|
||||
return ajaxSuccess(ctx, nil)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
const (
|
||||
// Version is the version of Swirl
|
||||
Version = "0.5.3"
|
||||
Version = "0.5.4"
|
||||
)
|
||||
|
||||
const (
|
||||
|
10
misc/perm.go
10
misc/perm.go
@ -91,12 +91,22 @@ var Perms = []PermGroup{
|
||||
{Key: "task.raw", Text: "View raw"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Image",
|
||||
Perms: []Perm{
|
||||
{Key: "image.list", Text: "View list"},
|
||||
{Key: "image.detail", Text: "View detail"},
|
||||
{Key: "image.raw", Text: "View raw"},
|
||||
{Key: "image.delete", Text: "Delete"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Container",
|
||||
Perms: []Perm{
|
||||
{Key: "container.list", Text: "View list"},
|
||||
{Key: "container.detail", Text: "View detail"},
|
||||
{Key: "container.raw", Text: "View raw"},
|
||||
{Key: "container.delete", Text: "Delete"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -575,6 +575,14 @@ func NewImageListInfo(image types.ImageSummary) *ImageListInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
type ContainerListArgs struct {
|
||||
// created|restarting|running|removing|paused|exited|dead
|
||||
Filter string `query:"filter"`
|
||||
Name string `query:"name"`
|
||||
PageIndex int `query:"page"`
|
||||
PageSize int `query:"size"`
|
||||
}
|
||||
|
||||
type ContainerListInfo struct {
|
||||
types.Container
|
||||
CreatedAt time.Time
|
||||
|
@ -19,6 +19,7 @@
|
||||
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li><a href="/">Dashboard</a></li>
|
||||
<li><a href="/container/">Containers</a></li>
|
||||
<li class="is-active"><a>Detail</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
@ -37,12 +38,9 @@
|
||||
<nav class="navbar has-shadow">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item is-tab is-active" href="/container/{{.Container.ContainerJSONBase.ID}}/detail">
|
||||
Detail
|
||||
</a>
|
||||
<a class="navbar-item is-tab " href="/container/{{.Container.ContainerJSONBase.ID}}/raw">
|
||||
Raw
|
||||
</a>
|
||||
<a class="navbar-item is-tab is-active" href="/container/{{.Container.ContainerJSONBase.ID}}/detail">Detail</a>
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/raw">Raw</a>
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/logs">Logs</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@ -64,6 +62,11 @@
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{{ yield tags(title="Labels", tags=.Container.Config.Labels) }}
|
||||
<a href="/container/" class="button is-primary">
|
||||
<span class="icon"><i class="fa fa-reply"></i></span>
|
||||
<span>Return</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
@ -1,6 +1,10 @@
|
||||
{{ extends "../_layouts/default" }}
|
||||
{{ import "../_modules/pager" }}
|
||||
|
||||
{{ block script() }}
|
||||
<script>$(() => new Swirl.Container.ListPage())</script>
|
||||
{{ end }}
|
||||
|
||||
{{ block body() }}
|
||||
<section class="hero is-info">
|
||||
<div class="hero-body">
|
||||
@ -38,14 +42,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right side -->
|
||||
{*<div class="level-right">*}
|
||||
{*<p class="level-item">*}
|
||||
{*<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fa fa-remove"></i></span><span>Delete</span></button>*}
|
||||
{*</p>*}
|
||||
{*<p class="level-item">*}
|
||||
{*<a class="button is-success" href="new"><span class="icon"><i class="fa fa-plus"></i></span><span>New</span></a>*}
|
||||
{*</p>*}
|
||||
{*</div>*}
|
||||
<div class="level-right">
|
||||
<p class="level-item">
|
||||
{{if .Filter == ""}}
|
||||
<strong>All</strong>
|
||||
{{else}}
|
||||
<a href="/container/">All</a>
|
||||
{{end}}
|
||||
</p>
|
||||
<p class="level-item">
|
||||
{{if .Filter == "running"}}
|
||||
<strong>Running</strong>
|
||||
{{else}}
|
||||
<a href="?filter=running">Running</a>
|
||||
{{end}}
|
||||
</p>
|
||||
<p class="level-item">
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fa fa-remove"></i></span><span>Delete</span></button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<table id="table-items" class="table is-bordered is-striped is-narrow is-fullwidth">
|
||||
@ -56,7 +71,7 @@
|
||||
<th>Image</th>
|
||||
<th>State</th>
|
||||
<th>Created</th>
|
||||
{*<th width="160">Action</th>*}
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -67,11 +82,11 @@
|
||||
<td>{{ limit(.Image, 50) }}</td>
|
||||
<td><span class="tag is-{{ .State == "running" ? "success" : "danger" }}">{{ .State }}</span></td>
|
||||
<td>{{ time(.CreatedAt) }}</td>
|
||||
{*<td>*}
|
||||
{*<div class="field has-addons">*}
|
||||
{*<p class="control"><button class="button is-small is-danger is-outlined" data-action="delete-service">Delete</button></p>*}
|
||||
{*</div>*}
|
||||
{*</td>*}
|
||||
<td>
|
||||
<div class="field has-addons">
|
||||
<p class="control"><button class="button is-small is-danger is-outlined" data-action="delete-container">Delete</button></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
|
107
views/container/logs.jet
Normal file
107
views/container/logs.jet
Normal file
@ -0,0 +1,107 @@
|
||||
{{ extends "../_layouts/default" }}
|
||||
|
||||
{{ block body() }}
|
||||
<section class="hero is-info">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title is-2">
|
||||
CONTAINER
|
||||
</h1>
|
||||
<h2 class="subtitle is-5">
|
||||
A container is a running instance of image.
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="container">
|
||||
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li><a href="/">Dashboard</a></li>
|
||||
<li><a href="/container/">Containers</a></li>
|
||||
<li class="is-active"><a>Raw</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<section class="hero is-small is-light">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h2 class="title is-2">
|
||||
{{ .Container.ContainerJSONBase.Name }}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<nav class="navbar has-shadow">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/detail">Detail</a>
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/raw">Raw</a>
|
||||
<a class="navbar-item is-tab is-active" href="/container/{{.Container.ContainerJSONBase.ID}}/logs">Logs</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="level">
|
||||
<form>
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<div class="field has-addons">
|
||||
<p class="control">
|
||||
<a class="button is-static">Lines</a>
|
||||
</p>
|
||||
<p class="control">
|
||||
<input name="line" value="{{ .Line }}" class="input" placeholder="Max lines from tail">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<div class="field">
|
||||
<input id="cb-timestamps" name="timestamps" value="true" type="checkbox" class="switch is-success is-rounded"{{if .Timestamps}} checked{{end}}>
|
||||
<label for="cb-timestamps">Add timestamps</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<div class="field">
|
||||
<p class="control">
|
||||
<button class="button is-primary">Search</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<div class="tabs is-boxed" data-target="tab-content">
|
||||
<ul>
|
||||
<li class="is-active">
|
||||
<a><span>Stdout</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a><span>Stderr</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="tab-content" class="content">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
{{ if .Stdout }}<textarea class="textarea code is-small" rows="30" readonly>{{ .Stdout }}</textarea>{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field" style="display: none">
|
||||
<div class="control">
|
||||
{{ if .Stderr }}<textarea class="textarea code is-small" rows="30" readonly>{{ .Stderr }}</textarea>{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/service/" class="button is-primary">
|
||||
<span class="icon"><i class="fa fa-reply"></i></span>
|
||||
<span>Return</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
@ -26,6 +26,7 @@
|
||||
<nav class="breadcrumb has-succeeds-separator is-small is-marginless" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li><a href="/">Dashboard</a></li>
|
||||
<li><a href="/container/">Containers</a></li>
|
||||
<li class="is-active"><a>Raw</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
@ -42,12 +43,9 @@
|
||||
<nav class="navbar has-shadow">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/detail">
|
||||
Detail
|
||||
</a>
|
||||
<a class="navbar-item is-tab is-active" href="/container/{{.Container.ContainerJSONBase.ID}}/raw">
|
||||
Raw
|
||||
</a>
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/detail">Detail</a>
|
||||
<a class="navbar-item is-tab is-active" href="/container/{{.Container.ContainerJSONBase.ID}}/raw">Raw</a>
|
||||
<a class="navbar-item is-tab" href="/container/{{.Container.ContainerJSONBase.ID}}/logs">Logs</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@ -56,6 +54,10 @@
|
||||
<div class="content">
|
||||
<pre class="is-paddingless"><code class="json">{{ .Raw }}</code></pre>
|
||||
</div>
|
||||
<a href="/container/" class="button is-primary">
|
||||
<span class="icon"><i class="fa fa-reply"></i></span>
|
||||
<span>Return</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
@ -57,7 +57,7 @@
|
||||
<th>Tags</th>
|
||||
<th>Size</th>
|
||||
<th>Created</th>
|
||||
<th width="160">Action</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -76,7 +76,7 @@
|
||||
<div class="card-content">
|
||||
{*{{yield progress(title="System", percent=0)}}*}
|
||||
{{yield progress(title="Image", percent=100)}}
|
||||
{{yield progress(title="Container", percent=35)}}
|
||||
{{yield progress(title="Container", percent=80)}}
|
||||
{{yield progress(title="Volume", percent=100)}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,7 +73,7 @@
|
||||
<td><a href="/volume/{{ .Name }}/detail">{{ limit(.Name, 30) }}</a></td>
|
||||
<td>{{ .Driver }}</td>
|
||||
<td>{{ .Scope }}</td>
|
||||
<td>{{ .Mountpoint }}</td>
|
||||
<td>{{ limit(.Mountpoint, 50) }}</td>
|
||||
<td>
|
||||
<div class="field has-addons">
|
||||
<p class="control"><button class="button is-small is-danger is-outlined" data-action="delete-volume">Delete</button></p>
|
||||
|
Loading…
Reference in New Issue
Block a user