Add edit pages of secret and config

This commit is contained in:
cuigh 2017-09-27 15:36:50 +08:00
parent 49eaee7cec
commit 605706b235
10 changed files with 294 additions and 45 deletions

View File

@ -5,6 +5,7 @@ import (
"sort"
"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/api/types/swarm"
@ -34,18 +35,47 @@ func ConfigList(name string, pageIndex, pageSize int) (configs []swarm.Config, t
}
// ConfigCreate create a config.
func ConfigCreate(name string, data []byte, labels map[string]string) error {
func ConfigCreate(info *model.ConfigCreateInfo) error {
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
// todo:
spec := swarm.ConfigSpec{}
spec.Name = name
spec.Data = data
spec.Labels = labels
spec.Name = info.Name
spec.Data = []byte(info.Data)
spec.Labels = info.Labels.ToMap()
_, err = cli.ConfigCreate(ctx, spec)
return
})
}
// ConfigUpdate update a config.
func ConfigUpdate(info *model.ConfigUpdateInfo) error {
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
var cfg swarm.Config
cfg, _, err = cli.ConfigInspectWithRaw(ctx, info.ID)
if err != nil {
return err
}
spec := cfg.Spec
// only the Labels field can be updated on API 1.30
//spec.Name = info.Name
//spec.Data = []byte(info.Data)
spec.Labels = info.Labels.ToMap()
return cli.ConfigUpdate(ctx, info.ID, cfg.Version, spec)
})
}
// ConfigInspect returns config information with raw data.
func ConfigInspect(id string) (cfg swarm.Config, raw []byte, err error) {
var (
ctx context.Context
cli *client.Client
)
if ctx, cli, err = mgr.Client(); err == nil {
cfg, raw, err = cli.ConfigInspectWithRaw(ctx, id)
}
return
}
// ConfigRemove remove a config.
func ConfigRemove(ids []string) error {
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {

View File

@ -4,11 +4,12 @@ import (
"context"
"sort"
"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/api/types/swarm"
"github.com/docker/docker/client"
"github.com/cuigh/swirl/misc"
)
// SecretList return all secrets.
@ -34,18 +35,47 @@ func SecretList(name string, pageIndex, pageSize int) (secrets []swarm.Secret, t
}
// SecretCreate create a secret.
func SecretCreate(name string, data []byte, labels map[string]string) error {
func SecretCreate(info *model.ConfigCreateInfo) error {
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
// todo:
spec := swarm.SecretSpec{}
spec.Name = name
spec.Data = data
spec.Labels = labels
spec.Name = info.Name
spec.Data = []byte(info.Data)
spec.Labels = info.Labels.ToMap()
_, err = cli.SecretCreate(ctx, spec)
return
})
}
// SecretUpdate update a config.
func SecretUpdate(info *model.ConfigUpdateInfo) error {
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {
var secret swarm.Secret
secret, _, err = cli.SecretInspectWithRaw(ctx, info.ID)
if err != nil {
return err
}
spec := secret.Spec
// only the Labels field can be updated on API 1.30
//spec.Name = info.Name
//spec.Data = []byte(info.Data)
spec.Labels = info.Labels.ToMap()
return cli.SecretUpdate(ctx, info.ID, secret.Version, spec)
})
}
// SecretInspect returns secret information with raw data.
func SecretInspect(id string) (secret swarm.Secret, raw []byte, err error) {
var (
ctx context.Context
cli *client.Client
)
if ctx, cli, err = mgr.Client(); err == nil {
secret, raw, err = cli.SecretInspectWithRaw(ctx, id)
}
return
}
// SecretRemove remove a secret.
func SecretRemove(id string) error {
return mgr.Do(func(ctx context.Context, cli *client.Client) (err error) {

View File

@ -5,6 +5,7 @@ import (
"github.com/cuigh/auxo/net/web"
"github.com/cuigh/auxo/util/cast"
"github.com/cuigh/swirl/biz"
"github.com/cuigh/swirl/biz/docker"
"github.com/cuigh/swirl/model"
)
@ -14,6 +15,8 @@ type ConfigController struct {
Delete web.HandlerFunc `path:"/delete" method:"post" name:"config.delete" authorize:"!" desc:"delete config"`
New web.HandlerFunc `path:"/new" name:"config.new" authorize:"!" desc:"new config page"`
Create web.HandlerFunc `path:"/new" method:"post" name:"config.create" authorize:"!" desc:"create config"`
Edit web.HandlerFunc `path:"/:id/edit" name:"config.edit" authorize:"!" desc:"edit config page"`
Update web.HandlerFunc `path:"/:id/update" method:"post" name:"config.update" authorize:"!" desc:"update config"`
}
func Config() (c *ConfigController) {
@ -45,23 +48,35 @@ func Config() (c *ConfigController) {
}
c.Create = func(ctx web.Context) error {
v := struct {
Name string `json:"name"`
Data string `json:"data"`
Labels []struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"labels"`
}{}
err := ctx.Bind(&v)
v := &model.ConfigCreateInfo{}
err := ctx.Bind(v)
if err == nil {
labels := make(map[string]string)
for _, l := range v.Labels {
if l.Name != "" && l.Value != "" {
labels[l.Name] = l.Value
}
err = docker.ConfigCreate(v)
if err == nil {
biz.Event.CreateConfig(model.EventActionCreate, v.Name, ctx.User())
}
}
return ajaxResult(ctx, err)
}
c.Edit = func(ctx web.Context) error {
id := ctx.P("id")
cfg, _, err := docker.ConfigInspect(id)
if err != nil {
return err
}
m := newModel(ctx).Add("Config", cfg)
return ctx.Render("config/edit", m)
}
c.Update = func(ctx web.Context) error {
v := &model.ConfigUpdateInfo{}
err := ctx.Bind(v)
if err == nil {
err = docker.ConfigUpdate(v)
if err == nil {
biz.Event.CreateConfig(model.EventActionUpdate, v.Name, ctx.User())
}
err = docker.ConfigCreate(v.Name, []byte(v.Data), labels)
}
return ajaxResult(ctx, err)
}

View File

@ -15,6 +15,8 @@ type SecretController struct {
Delete web.HandlerFunc `path:"/delete" method:"post" name:"secret.delete" authorize:"!" desc:"delete secret"`
New web.HandlerFunc `path:"/new" name:"secret.new" authorize:"!" desc:"new secret page"`
Create web.HandlerFunc `path:"/new" method:"post" name:"secret.create" authorize:"!" desc:"create secret"`
Edit web.HandlerFunc `path:"/:id/edit" name:"secret.edit" authorize:"!" desc:"edit secret page"`
Update web.HandlerFunc `path:"/:id/update" method:"post" name:"secret.update" authorize:"!" desc:"update secret"`
}
func Secret() (c *SecretController) {
@ -54,23 +56,10 @@ func Secret() (c *SecretController) {
}
c.Create = func(ctx web.Context) error {
v := struct {
Name string `json:"name"`
Data string `json:"data"`
Labels []struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"labels"`
}{}
err := ctx.Bind(&v)
v := &model.ConfigCreateInfo{}
err := ctx.Bind(v)
if err == nil {
labels := make(map[string]string)
for _, l := range v.Labels {
if l.Name != "" && l.Value != "" {
labels[l.Name] = l.Value
}
}
err = docker.SecretCreate(v.Name, []byte(v.Data), labels)
err = docker.SecretCreate(v)
if err == nil {
biz.Event.CreateSecret(model.EventActionCreate, v.Name, ctx.User())
}
@ -78,5 +67,27 @@ func Secret() (c *SecretController) {
return ajaxResult(ctx, err)
}
c.Edit = func(ctx web.Context) error {
id := ctx.P("id")
secret, _, err := docker.SecretInspect(id)
if err != nil {
return err
}
m := newModel(ctx).Add("Secret", secret)
return ctx.Render("secret/edit", m)
}
c.Update = func(ctx web.Context) error {
v := &model.ConfigUpdateInfo{}
err := ctx.Bind(v)
if err == nil {
err = docker.SecretUpdate(v)
if err == nil {
biz.Event.CreateSecret(model.EventActionUpdate, v.Name, ctx.User())
}
}
return ajaxResult(ctx, err)
}
return
}

View File

@ -104,8 +104,10 @@ var Perms = []PermGroup{
Perms: []Perm{
{Key: "secret.list", Text: "View list"},
{Key: "secret.new", Text: "View new"},
{Key: "secret.edit", Text: "View edit"},
{Key: "secret.create", Text: "Create"},
{Key: "secret.delete", Text: "Delete"},
{Key: "secret.update", Text: "Update"},
},
},
{
@ -113,8 +115,10 @@ var Perms = []PermGroup{
Perms: []Perm{
{Key: "config.list", Text: "View list"},
{Key: "config.new", Text: "View new"},
{Key: "config.edit", Text: "View edit"},
{Key: "config.create", Text: "Create"},
{Key: "config.delete", Text: "Delete"},
{Key: "config.update", Text: "Update"},
},
},
{

View File

@ -3,11 +3,11 @@ package model
import (
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"time"
"fmt"
"strconv"
"os"
"github.com/cuigh/auxo/data/size"
"github.com/docker/docker/api/types"
@ -466,6 +466,17 @@ func (pc *PlacementConstraint) ToConstraint() string {
return ""
}
type ConfigCreateInfo struct {
Name string `json:"name"`
Data string `json:"data"`
Labels Options `json:"labels"`
}
type ConfigUpdateInfo struct {
ID string `json:"id"`
ConfigCreateInfo
}
type TaskInfo struct {
swarm.Task
NodeName string

76
views/config/edit.jet Normal file
View File

@ -0,0 +1,76 @@
{{ extends "../_layouts/default" }}
{{ import "../_modules/form" }}
{{ block script() }}
<script>$(() => new Swirl.Config.NewPage())</script>
{{ end }}
{{ block body() }}
<section class="hero is-info">
<div class="hero-body">
<div class="container has-text-centered">
<h1 class="title is-2">
CONFIG
</h1>
<h2 class="subtitle is-5">
Configs are non-sensitive information that can be used by services, such as configuration files.
</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="/config/">Configs</a></li>
<li class="is-active"><a>Edit</a></li>
</ul>
</nav>
</div>
<section class="hero is-small is-light">
<div class="hero-body">
<div class="container">
<h2 class="title is-2">
{{ .Config.Spec.Name }}
</h2>
</div>
</div>
</section>
<nav class="navbar has-shadow">
<div class="container">
<div class="navbar-brand">
{*<a class="navbar-item is-tab" href="/config/{{.Config.ID}}/detail">Detail</a>*}
{*<a class="navbar-item is-tab" href="/config/{{.Config.ID}}/raw">Raw</a>*}
<a class="navbar-item is-tab is-active" href="/node/{{.Config.ID}}/edit">Edit</a>
</div>
</div>
</nav>
<section class="section">
<div class="container">
<form method="post" action="update" data-form="ajax-json" data-form="ajax-json" data-url="/config/">
<input name="id" value="{{ .Config.ID }}" type="hidden">
{*<div class="field">*}
{*<label class="label">Data</label>*}
{*<div class="control">*}
{*<textarea name="data" class="textarea" rows="12" placeholder="Config file content" data-v-rule="native" required></textarea>*}
{*</div>*}
{*</div>*}
<div class="field">
<label class="label">Labels</label>
{{ yield options(name="label", items=.Config.Spec.Labels) }}
</div>
<div class="field is-grouped">
<div class="control">
<button type="submit" class="button is-primary">Submit</button>
</div>
<div class="control">
<a href="/config/" class="button is-link">Cancel</a>
</div>
</div>
</form>
</div>
</section>
{{ end }}

View File

@ -71,6 +71,7 @@
<td>{{time(.CreatedAt)}}</td>
<td>{{time(.UpdatedAt)}}</td>
<td>
<a href="{{.ID}}/edit" class="button is-small is-dark is-outlined">Edit</a>
<button class="button is-small is-danger is-outlined" data-action="delete-config">Delete</button>
</td>
</tr>

70
views/secret/edit.jet Normal file
View File

@ -0,0 +1,70 @@
{{ extends "../_layouts/default" }}
{{ import "../_modules/form" }}
{{ block script() }}
<script>$(() => new Swirl.Secret.NewPage())</script>
{{ end }}
{{ block body() }}
<section class="hero is-info">
<div class="hero-body">
<div class="container has-text-centered">
<h1 class="title is-2">
SECRET
</h1>
<h2 class="subtitle is-5">
Secrets are sensitive data that can be used by services.
</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="/secret/">Secrets</a></li>
<li class="is-active"><a>Edit</a></li>
</ul>
</nav>
</div>
<section class="hero is-small is-light">
<div class="hero-body">
<div class="container">
<h2 class="title is-2">
{{ .Secret.Spec.Name }}
</h2>
</div>
</div>
</section>
<nav class="navbar has-shadow">
<div class="container">
<div class="navbar-brand">
{*<a class="navbar-item is-tab" href="/secret/{{.Secret.ID}}/detail">Detail</a>*}
{*<a class="navbar-item is-tab" href="/secret/{{.Secret.ID}}/raw">Raw</a>*}
<a class="navbar-item is-tab is-active" href="/node/{{.Secret.ID}}/edit">Edit</a>
</div>
</div>
</nav>
<section class="section">
<div class="container">
<form method="post" action="update" data-form="ajax-json" data-form="ajax-json" data-url="/secret/">
<input name="id" value="{{ .Secret.ID }}" type="hidden">
<div class="field">
<label class="label">Labels</label>
{{ yield options(name="label", items=.Secret.Spec.Labels) }}
</div>
<div class="field is-grouped">
<div class="control">
<button type="submit" class="button is-primary">Submit</button>
</div>
<div class="control">
<a href="/secret/" class="button is-link">Cancel</a>
</div>
</div>
</form>
</div>
</section>
{{ end }}

View File

@ -71,6 +71,7 @@
<td>{{time(.CreatedAt)}}</td>
<td>{{time(.UpdatedAt)}}</td>
<td>
<a href="{{.ID}}/edit" class="button is-small is-dark is-outlined">Edit</a>
<button class="button is-small is-danger is-outlined" data-action="delete-secret">Delete</button>
</td>
</tr>