mirror of
https://github.com/cuigh/swirl
synced 2025-03-03 18:51:34 +00:00
Support restarting service
This commit is contained in:
parent
2277893dfe
commit
536dc59cc6
@ -1145,7 +1145,7 @@ var Swirl;
|
||||
<p class="card-header-title is-paddingless">${this.opts.title}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
<i class="fas fa-trash has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</header>
|
||||
@ -2339,7 +2339,7 @@ var Swirl;
|
||||
<td>
|
||||
<a class="button is-small is-outlined is-danger" data-action="delete-${this.name}">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-times"></i>
|
||||
<i class="fas fa-trash"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
@ -2416,7 +2416,8 @@ var Swirl;
|
||||
this.table = new Table("#table-items");
|
||||
this.table.on("delete-service", this.deleteService.bind(this))
|
||||
.on("scale-service", this.scaleService.bind(this))
|
||||
.on("rollback-service", this.rollbackService.bind(this));
|
||||
.on("rollback-service", this.rollbackService.bind(this))
|
||||
.on("restart-service", this.restartService.bind(this));
|
||||
}
|
||||
deleteService(e) {
|
||||
let $tr = $(e.target).closest("tr");
|
||||
@ -2429,23 +2430,30 @@ var Swirl;
|
||||
});
|
||||
}
|
||||
scaleService(e) {
|
||||
let $btn = $(e.target).closest("button");
|
||||
let $tr = $btn.closest("tr");
|
||||
let $target = $(e.target), $tr = $target.closest("tr");
|
||||
let data = {
|
||||
name: $tr.find("td:eq(0)").text().trim(),
|
||||
count: $btn.data("replicas"),
|
||||
count: $target.data("replicas"),
|
||||
};
|
||||
Modal.confirm(`<input name="count" value="${data.count}" class="input" placeholder="Replicas">`, "Scale service", (dlg, e) => {
|
||||
Modal.confirm(`<input name="count" value="${data.count}" class="input" placeholder="Replicas">`, "Scale service", dlg => {
|
||||
data.count = dlg.find("input[name=count]").val();
|
||||
$ajax.post(`${data.name}/scale`, data).trigger(e.target).encoder("form").json(() => {
|
||||
$ajax.post(`${data.name}/scale`, data).encoder("form").json(() => {
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
}
|
||||
rollbackService(e) {
|
||||
let $btn = $(e.target).closest("button"), $tr = $btn.closest("tr"), name = $tr.find("td:eq(0)").text().trim();
|
||||
Modal.confirm(`Are you sure to rollback service: <strong>${name}</strong>?`, "Rollback service", (dlg, e) => {
|
||||
$ajax.post(`${name}/rollback`, { name: name }).trigger(e.target).encoder("form").json(() => {
|
||||
let $tr = $(e.target).closest("tr"), name = $tr.find("td:eq(0)").text().trim();
|
||||
Modal.confirm(`Are you sure to rollback service: <strong>${name}</strong>?`, "Rollback service", dlg => {
|
||||
$ajax.post(`${name}/rollback`, { name: name }).encoder("form").json(() => {
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
restartService(e) {
|
||||
let $tr = $(e.target).closest("tr"), name = $tr.find("td:eq(0)").text().trim();
|
||||
Modal.confirm(`Are you sure to restart service: <strong>${name}</strong>?`, "Restart service", dlg => {
|
||||
$ajax.post(`${name}/restart`, { name: name }).encoder("form").json(() => {
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
@ -31,7 +31,7 @@ namespace Swirl.Core {
|
||||
<p class="card-header-title is-paddingless">${this.opts.title}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
<i class="fas fa-trash has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</header>
|
||||
|
@ -176,7 +176,7 @@ namespace Swirl.Service {
|
||||
<td>
|
||||
<a class="button is-small is-outlined is-danger" data-action="delete-${this.name}">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-times"></i>
|
||||
<i class="fas fa-trash"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
|
@ -13,7 +13,8 @@ namespace Swirl.Service {
|
||||
// bind events
|
||||
this.table.on("delete-service", this.deleteService.bind(this))
|
||||
.on("scale-service", this.scaleService.bind(this))
|
||||
.on("rollback-service", this.rollbackService.bind(this));
|
||||
.on("rollback-service", this.rollbackService.bind(this))
|
||||
.on("restart-service", this.restartService.bind(this));
|
||||
}
|
||||
|
||||
private deleteService(e: JQueryEventObject) {
|
||||
@ -28,26 +29,35 @@ namespace Swirl.Service {
|
||||
}
|
||||
|
||||
private scaleService(e: JQueryEventObject) {
|
||||
let $btn = $(e.target).closest("button");
|
||||
let $tr = $btn.closest("tr");
|
||||
let $target = $(e.target),
|
||||
$tr = $target.closest("tr");
|
||||
let data = {
|
||||
name: $tr.find("td:eq(0)").text().trim(),
|
||||
count: $btn.data("replicas"),
|
||||
count: $target.data("replicas"),
|
||||
};
|
||||
Modal.confirm(`<input name="count" value="${data.count}" class="input" placeholder="Replicas">`, "Scale service", (dlg, e) => {
|
||||
Modal.confirm(`<input name="count" value="${data.count}" class="input" placeholder="Replicas">`, "Scale service", dlg => {
|
||||
data.count = dlg.find("input[name=count]").val();
|
||||
$ajax.post(`${data.name}/scale`, data).trigger(e.target).encoder("form").json<AjaxResult>(() => {
|
||||
$ajax.post(`${data.name}/scale`, data).encoder("form").json<AjaxResult>(() => {
|
||||
location.reload();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private rollbackService(e: JQueryEventObject) {
|
||||
let $btn = $(e.target).closest("button"),
|
||||
$tr = $btn.closest("tr"),
|
||||
let $tr = $(e.target).closest("tr"),
|
||||
name = $tr.find("td:eq(0)").text().trim();
|
||||
Modal.confirm(`Are you sure to rollback service: <strong>${name}</strong>?`, "Rollback service", (dlg, e) => {
|
||||
$ajax.post(`${name}/rollback`, {name: name}).trigger(e.target).encoder("form").json<AjaxResult>(() => {
|
||||
Modal.confirm(`Are you sure to rollback service: <strong>${name}</strong>?`, "Rollback service", dlg => {
|
||||
$ajax.post(`${name}/rollback`, {name: name}).encoder("form").json<AjaxResult>(() => {
|
||||
dlg.close();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private restartService(e: JQueryEventObject) {
|
||||
let $tr = $(e.target).closest("tr"),
|
||||
name = $tr.find("td:eq(0)").text().trim();
|
||||
Modal.confirm(`Are you sure to restart service: <strong>${name}</strong>?`, "Restart service", dlg => {
|
||||
$ajax.post(`${name}/restart`, {name: name}).encoder("form").json<AjaxResult>(() => {
|
||||
dlg.close();
|
||||
})
|
||||
});
|
||||
|
@ -340,19 +340,15 @@ func ServiceScale(name string, version, count uint64) error {
|
||||
|
||||
spec := service.Spec
|
||||
if spec.Mode.Replicated == nil {
|
||||
return errors.New("the mode of service isn't replicated")
|
||||
return errors.New("scale can only be used with replicated mode")
|
||||
}
|
||||
spec.Mode.Replicated.Replicas = &count
|
||||
|
||||
options := types.ServiceUpdateOptions{
|
||||
RegistryAuthFrom: types.RegistryAuthFromSpec,
|
||||
QueryRegistry: false,
|
||||
}
|
||||
ver := service.Version
|
||||
if version > 0 {
|
||||
ver = swarm.Version{Index: version}
|
||||
}
|
||||
resp, err := cli.ServiceUpdate(context.Background(), name, ver, spec, options)
|
||||
resp, err := cli.ServiceUpdate(context.Background(), name, ver, spec, types.ServiceUpdateOptions{})
|
||||
if err == nil && len(resp.Warnings) > 0 {
|
||||
mgr.Logger().Warnf("service %s was scaled but got warnings: %v", name, resp.Warnings)
|
||||
}
|
||||
@ -734,20 +730,30 @@ func ServiceRollback(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if service.PreviousSpec == nil {
|
||||
return errors.New("can't rollback service, previous spec is not exists")
|
||||
}
|
||||
|
||||
spec := *service.PreviousSpec
|
||||
options := types.ServiceUpdateOptions{
|
||||
RegistryAuthFrom: types.RegistryAuthFromPreviousSpec,
|
||||
QueryRegistry: false,
|
||||
Rollback: "previous",
|
||||
Rollback: "previous",
|
||||
}
|
||||
resp, err := cli.ServiceUpdate(context.Background(), name, service.Version, spec, options)
|
||||
resp, err := cli.ServiceUpdate(context.Background(), name, service.Version, service.Spec, options)
|
||||
if err == nil && len(resp.Warnings) > 0 {
|
||||
mgr.Logger().Warnf("service %s was rollbacked but got warnings: %v", name, resp.Warnings)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// ServiceRestart force to refresh a service.
|
||||
func ServiceRestart(name string) error {
|
||||
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
|
||||
service, _, err := cli.ServiceInspectWithRaw(ctx, name, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.Spec.TaskTemplate.ForceUpdate++
|
||||
resp, err := cli.ServiceUpdate(context.Background(), name, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||
if err == nil && len(resp.Warnings) > 0 {
|
||||
mgr.Logger().Warnf("service %s was restarted but got warnings: %v", name, resp.Warnings)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ button.return: Return
|
||||
button.leave: Leave
|
||||
button.scale: Scale
|
||||
button.rollback: Rollback
|
||||
button.restart: Restart
|
||||
button.previous: Previous
|
||||
button.next: Next
|
||||
button.import: Import
|
||||
@ -71,7 +72,7 @@ menu.stack.task: Tasks
|
||||
menu.stack.archive: Archives
|
||||
menu.task: Tasks
|
||||
menu.secret: Secrets
|
||||
menu.config: Config
|
||||
menu.config: Configs
|
||||
menu.system: System
|
||||
menu.role: Roles
|
||||
menu.user: Users
|
||||
|
@ -20,6 +20,7 @@ button.return: 返回
|
||||
button.leave: 离开
|
||||
button.scale: 调整
|
||||
button.rollback: 回退
|
||||
button.restart: 重启
|
||||
button.previous: 前一页
|
||||
button.next: 后一页
|
||||
button.import: 导入
|
||||
|
@ -24,6 +24,7 @@ type ServiceController struct {
|
||||
Delete web.HandlerFunc `path:"/:name/delete" method:"post" name:"service.delete" authorize:"!" perm:"write,service,name"`
|
||||
Scale web.HandlerFunc `path:"/:name/scale" method:"post" name:"service.scale" authorize:"!" perm:"write,service,name"`
|
||||
Rollback web.HandlerFunc `path:"/:name/rollback" method:"post" name:"service.rollback" authorize:"!" perm:"write,service,name"`
|
||||
Restart web.HandlerFunc `path:"/:name/restart" method:"post" name:"service.restart" authorize:"!" perm:"write,service,name"`
|
||||
New web.HandlerFunc `path:"/new" name:"service.new" authorize:"!" desc:"new service page"`
|
||||
Create web.HandlerFunc `path:"/new" method:"post" name:"service.create" authorize:"!" desc:"create service"`
|
||||
Edit web.HandlerFunc `path:"/:name/edit" name:"service.edit" authorize:"!" perm:"write,service,name"`
|
||||
@ -47,6 +48,7 @@ func Service() (c *ServiceController) {
|
||||
Update: serviceUpdate,
|
||||
Scale: serviceScale,
|
||||
Rollback: serviceRollback,
|
||||
Restart: serviceRestart,
|
||||
PermEdit: servicePermEdit,
|
||||
PermUpdate: permUpdate("service", "name"),
|
||||
Stats: serviceStats,
|
||||
@ -271,6 +273,15 @@ func serviceRollback(ctx web.Context) error {
|
||||
return ajaxResult(ctx, err)
|
||||
}
|
||||
|
||||
func serviceRestart(ctx web.Context) error {
|
||||
name := ctx.F("name")
|
||||
err := docker.ServiceRestart(name)
|
||||
if err == nil {
|
||||
biz.Event.CreateService(model.EventActionRestart, name, ctx.User())
|
||||
}
|
||||
return ajaxResult(ctx, err)
|
||||
}
|
||||
|
||||
func servicePermEdit(ctx web.Context) error {
|
||||
name := ctx.P("name")
|
||||
m := newModel(ctx).Set("Name", name)
|
||||
|
@ -36,6 +36,7 @@ const (
|
||||
EventActionUpdate EventAction = "Update"
|
||||
EventActionScale EventAction = "Scale"
|
||||
EventActionRollback EventAction = "Rollback"
|
||||
EventActionRestart EventAction = "Restart"
|
||||
EventActionDisconnect EventAction = "Disconnect"
|
||||
)
|
||||
|
||||
|
@ -261,7 +261,7 @@
|
||||
<td>
|
||||
<a class="button is-small is-outlined is-danger" data-action="delete-secret">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-times"></i>
|
||||
<i class="fas fa-trash"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
@ -301,7 +301,7 @@
|
||||
<td>
|
||||
<a class="button is-small is-outlined is-danger" data-action="delete-config">
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-times"></i>
|
||||
<i class="fas fa-trash"></i>
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
|
@ -40,7 +40,7 @@
|
||||
<!-- Right side -->
|
||||
<div class="level-right">
|
||||
<p class="level-item">
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-times"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-trash"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
</p>
|
||||
<p class="level-item">
|
||||
<a href="new" class="button is-success"><span class="icon"><i class="fas fa-plus"></i></span><span>{{ i18n("button.new") }}</span></a>
|
||||
|
@ -54,7 +54,7 @@
|
||||
{{end}}
|
||||
</p>
|
||||
<p class="level-item">
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-times"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-trash"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<!-- Right side -->
|
||||
<div class="level-right">
|
||||
<p class="level-item">
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-times"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-trash"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -40,7 +40,7 @@
|
||||
<!-- Right side -->
|
||||
<div class="level-right">
|
||||
<p class="level-item">
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-times"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-trash"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
</p>
|
||||
<p class="level-item">
|
||||
<a href="new" class="button is-success"><span class="icon"><i class="fas fa-plus"></i></span><span>{{ i18n("button.new") }}</span></a>
|
||||
|
@ -44,7 +44,7 @@
|
||||
<th>{{ i18n("field.image") }}</th>
|
||||
<th width="145">Mode</th>
|
||||
<th>{{ i18n("field.updated-at") }}</th>
|
||||
<th width="140">{{ i18n("field.action") }}</th>
|
||||
<th width="190">{{ i18n("field.action") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -60,22 +60,28 @@
|
||||
</td>
|
||||
<td>{{time(.UpdatedAt)}}{{ if .UpdateStatus }}<span class="icon {{ if .UpdateStatus == "completed" }}has-text-success{{ else if .UpdateStatus == "updating" }}has-text-warning{{ else }}has-text-danger{{ end }} tooltip" data-tooltip="{{.UpdateStatus}}"><i class="fas fa-circle"></i></span>{{ end }}</td>
|
||||
<td>
|
||||
<a href="{{.Name}}/edit" class="button is-small is-dark is-outlined tooltip is-tooltip-bottom" data-tooltip="{{ i18n("button.edit") }}">
|
||||
<span class="icon"><i class="fas fa-edit"></i></span>
|
||||
</a>
|
||||
{{if .Mode == "replicated"}}
|
||||
<button type="button" class="button is-small is-info is-outlined tooltip is-tooltip-bottom" data-tooltip="{{ i18n("button.scale") }}" data-action="scale-service" data-replicas="{{.Replicas}}">
|
||||
<span class="icon"><i class="fas fa-arrows-alt"></i></span>
|
||||
</button>
|
||||
{{end}}
|
||||
{{if .Rollback }}
|
||||
<button type="button" class="button is-small is-info is-outlined tooltip is-tooltip-bottom" data-tooltip="{{ i18n("button.rollback") }}" data-action="rollback-service">
|
||||
<span class="icon"><i class="fas fa-history"></i></span>
|
||||
</button>
|
||||
{{end}}
|
||||
<button class="button is-small is-danger is-outlined tooltip is-tooltip-bottom" data-tooltip="{{ i18n("button.delete") }}" data-action="delete-service">
|
||||
<span class="icon"><i class="fas fa-times"></i></span>
|
||||
</button>
|
||||
<a href="{{.Name}}/edit" class="button is-small is-dark is-outlined">{{ i18n("button.edit") }}</a>
|
||||
<button class="button is-small is-danger is-outlined" data-action="delete-service">{{ i18n("button.delete") }}</button>
|
||||
<div class="dropdown is-hoverable is-right">
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu-{{ .Name }}">
|
||||
<span>More</span>
|
||||
<span class="icon is-small">
|
||||
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||
{*<i class="fas fa-ellipsis-v" aria-hidden="true"></i>*}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="dropdown-menu-{{ .Name }}" role="menu">
|
||||
<div class="dropdown-content">
|
||||
{{if .Mode == "replicated" }}<a class="dropdown-item" data-action="scale-service" data-replicas="{{.Replicas}}">{{ i18n("button.scale") }}</a>{{end}}
|
||||
{{if .Rollback }}<a class="dropdown-item" data-action="rollback-service">{{ i18n("button.rollback") }}</a>{{end}}
|
||||
<a class="dropdown-item" data-action="restart-service">{{ i18n("button.restart") }}</a>
|
||||
{*<a class="dropdown-item has-text-danger" data-action="delete-service">{{ i18n("button.delete") }}</a>*}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
@ -59,7 +59,7 @@
|
||||
<span class="icon"><i class="fas fa-edit"></i></span>
|
||||
</a>
|
||||
<button class="button is-small is-danger is-outlined tooltip is-tooltip-bottom" data-tooltip="{{ i18n("button.delete") }}" data-action="delete-template">
|
||||
<span class="icon"><i class="fas fa-times"></i></span>
|
||||
<span class="icon"><i class="fas fa-trash"></i></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -12,7 +12,7 @@
|
||||
<div class="field">
|
||||
<label class="label">{{ i18n("field.name") }}</label>
|
||||
<div class="control">
|
||||
<input name="name" value="{{ .Chart.Name }}" class="input" placeholder="lower-case letters and underline only" data-v-rule="native;regex" data-v-arg-regex="[a-z_]+" data-v-msg-regex="Only lower-case letters and underline are allowed." required{{if .Chart.Name}} readonly{{ end }}>
|
||||
<input name="name" value="{{ .Chart.Name }}" class="input" placeholder="Only lower-case letters and underscores are allowed" data-v-rule="native;regex" data-v-arg-regex="[a-z_]+" data-v-msg-regex="Only lower-case letters and underline are allowed." required{{if .Chart.Name}} readonly{{ end }}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
@ -97,7 +97,7 @@
|
||||
<div class="field">
|
||||
<label class="label">Query expression</label>
|
||||
<div class="control">
|
||||
<input name="query" value="{{ .Chart.Query }}" class="input" placeholder="Prometheus query expression" data-v-rule="native" required>
|
||||
<input name="query" value="{{ .Chart.Query }}" class="input" placeholder="Prometheus query expression, for service dashboard, you can use '${service}' variable" data-v-rule="native" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
|
@ -40,10 +40,10 @@
|
||||
<!-- Right side -->
|
||||
<div class="level-right">
|
||||
<p class="level-item">
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-times"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
<button id="btn-delete" class="button is-danger"><span class="icon"><i class="fas fa-trash"></i></span><span>{{ i18n("button.delete") }}</span></button>
|
||||
</p>
|
||||
<p class="level-item">
|
||||
<button id="btn-prune" class="button is-warning"><span class="icon"><i class="fas fa-times-circle"></i></span><span>{{ i18n("button.prune") }}</span></button>
|
||||
<button id="btn-prune" class="button is-warning"><span class="icon"><i class="fas fa-trash-alt"></i></span><span>{{ i18n("button.prune") }}</span></button>
|
||||
</p>
|
||||
<p class="level-item">
|
||||
<a class="button is-success" href="new"><span class="icon"><i class="fas fa-plus"></i></span><span>{{ i18n("button.new") }}</span></a>
|
||||
|
Loading…
Reference in New Issue
Block a user