Display command line for creating service on detail page

This commit is contained in:
cuigh 2017-10-20 14:45:25 +08:00
parent 6d820149cf
commit a4ad885258
4 changed files with 174 additions and 6 deletions

View File

@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/cuigh/auxo/ext/texts"
"github.com/cuigh/swirl/misc"
"github.com/cuigh/swirl/model"
"github.com/docker/docker/api/types"
@ -549,3 +550,150 @@ func ServiceLogs(name string, line int, timestamps bool) (stdout, stderr *bytes.
}
return
}
// todo: support auto wrapping
// ServiceCommand returns the docker command line to create this service.
func ServiceCommand(name string) (cmd string, err error) {
var (
ctx context.Context
cli *client.Client
)
ctx, cli, err = mgr.Client()
if err != nil {
return
}
service, _, err := cli.ServiceInspectWithRaw(ctx, name, types.ServiceInspectOptions{})
if err != nil {
return
}
si := model.NewServiceInfo(service)
b := texts.GetBuilder()
b.Append("docker service create --name ", si.Name)
if si.Mode == "global" {
b.Append(" --mode global")
} else if si.Replicas > 1 {
b.AppendFormat(" --replicas %d", si.Replicas)
}
for _, n := range service.Spec.TaskTemplate.Networks {
var network types.NetworkResource
network, err = cli.NetworkInspect(ctx, n.Target, types.NetworkInspectOptions{})
b.Append(" --network ", network.Name)
}
if len(si.Endpoint.Ports) > 0 {
if si.Endpoint.Mode == swarm.ResolutionModeDNSRR {
b.AppendFormat(" --endpoint-mode %s", si.Endpoint.Mode)
}
for _, p := range si.Endpoint.Ports {
b.AppendFormat(" --publish mode=%s,target=%d,published=%d,protocol=%s", p.PublishMode, p.TargetPort, p.PublishedPort, p.Protocol)
}
}
for _, c := range si.Placement.Constraints {
b.AppendFormat(" --constraint '%s'", c.ToConstraint())
}
for _, p := range si.Placement.Preferences {
b.AppendFormat(" --placement-pref '%s'", p.Spread)
}
for _, e := range si.Environments {
b.AppendFormat(" --env '%s=%s'", e.Name, e.Value)
}
for _, l := range si.ServiceLabels {
b.AppendFormat(" --label '%s=%s'", l.Name, l.Value)
}
for _, l := range si.ContainerLabels {
b.AppendFormat(" --container-label '%s=%s'", l.Name, l.Value)
}
for _, mnt := range si.Mounts {
// todo: add mnt.Propagation
b.AppendFormat(" --mount type=%s,source=%s,destination=%s", mnt.Type, mnt.Source, mnt.Target)
if mnt.ReadOnly {
b.Append(",ro=1")
}
}
// todo: uid/gid
for _, c := range si.Configs {
b.AppendFormat(" --config source=%s,target=%s,mode=0%d", c.Name, c.FileName, c.Mode)
}
for _, c := range si.Secrets {
b.AppendFormat(" --secret source=%s,target=%s,mode=0%d", c.Name, c.FileName, c.Mode)
}
if si.Resource.Limit.IsSet() {
if si.Resource.Limit.CPU > 0 {
b.AppendFormat(" --limit-cpu %.2f", si.Resource.Limit.CPU)
}
if si.Resource.Limit.Memory != "" {
b.Append(" --limit-memory ", si.Resource.Limit.Memory)
}
}
if si.Resource.Reserve.IsSet() {
if si.Resource.Reserve.CPU > 0 {
b.AppendFormat(" --reserve-cpu %.2f", si.Resource.Reserve.CPU)
}
if si.Resource.Reserve.Memory != "" {
b.Append(" --reserve-memory ", si.Resource.Reserve.Memory)
}
}
if si.LogDriver.Name != "" {
b.Append(" --log-driver ", si.LogDriver.Name)
for _, opt := range si.LogDriver.Options {
b.AppendFormat(" --log-opt '%s=%s'", opt.Name, opt.Value)
}
}
// UpdatePolicy
if si.UpdatePolicy.Parallelism > 0 {
b.AppendFormat(" --update-parallelism %d", si.UpdatePolicy.Parallelism)
}
if si.UpdatePolicy.Delay != "" {
b.Append(" --update-delay ", si.UpdatePolicy.Delay)
}
if si.UpdatePolicy.FailureAction != "" {
b.Append(" --update-failure-action ", si.UpdatePolicy.FailureAction)
}
if si.UpdatePolicy.Order != "" {
b.Append(" --update-order ", si.UpdatePolicy.Order)
}
// RollbackPolicy
if si.RollbackPolicy.Parallelism > 0 {
b.AppendFormat(" --rollback-parallelism %d", si.RollbackPolicy.Parallelism)
}
if si.RollbackPolicy.Delay != "" {
b.Append(" --rollback-delay ", si.RollbackPolicy.Delay)
}
if si.RollbackPolicy.FailureAction != "" {
b.Append(" --rollback-failure-action ", si.RollbackPolicy.FailureAction)
}
if si.RollbackPolicy.Order != "" {
b.Append(" --rollback-order ", si.RollbackPolicy.Order)
}
// RestartPolicy
if si.RestartPolicy.Condition != "" {
b.AppendFormat(" --restart-condition %s", si.RestartPolicy.Condition)
}
if si.RestartPolicy.MaxAttempts > 0 {
b.AppendFormat(" --restart-max-attempts %d", si.RestartPolicy.MaxAttempts)
}
if si.RestartPolicy.Delay != "" {
b.Append(" --restart-delay ", si.RestartPolicy.Delay)
}
if si.RestartPolicy.Window != "" {
b.Append(" --restart-window ", si.RestartPolicy.Window)
}
//b.Append(" --with-registry-auth")
if si.Dir != "" {
b.Append(" --workdir ", si.Dir)
}
if si.User != "" {
b.Append(" --user ", si.User)
}
b.Append(" ", si.Image)
if si.Command != "" {
b.Append(" ", si.Command)
}
if si.Args != "" {
b.Append(" ", si.Args)
}
cmd = b.String()
return
}

View File

@ -80,7 +80,12 @@ func serviceDetail(ctx web.Context) error {
return err
}
m := newModel(ctx).Add("Service", info).Add("Tasks", tasks)
cmd, err := docker.ServiceCommand(name)
if err != nil {
return err
}
m := newModel(ctx).Add("Service", info).Add("Tasks", tasks).Add("Command", cmd)
return ctx.Render("service/detail", m)
}

View File

@ -282,13 +282,17 @@ func NewServiceInfo(service swarm.Service) *ServiceInfo {
}
if spec.UpdateConfig != nil {
si.UpdatePolicy.Parallelism = spec.UpdateConfig.Parallelism
si.UpdatePolicy.Delay = spec.UpdateConfig.Delay.String()
if spec.UpdateConfig.Delay > 0 {
si.UpdatePolicy.Delay = spec.UpdateConfig.Delay.String()
}
si.UpdatePolicy.FailureAction = spec.UpdateConfig.FailureAction
si.UpdatePolicy.Order = spec.UpdateConfig.Order
}
if spec.RollbackConfig != nil {
si.RollbackPolicy.Parallelism = spec.RollbackConfig.Parallelism
si.RollbackPolicy.Delay = spec.RollbackConfig.Delay.String()
if spec.RollbackConfig.Delay > 0 {
si.RollbackPolicy.Delay = spec.RollbackConfig.Delay.String()
}
si.RollbackPolicy.FailureAction = spec.RollbackConfig.FailureAction
si.RollbackPolicy.Order = spec.RollbackConfig.Order
}
@ -297,10 +301,10 @@ func NewServiceInfo(service swarm.Service) *ServiceInfo {
if spec.TaskTemplate.RestartPolicy.MaxAttempts != nil {
si.RestartPolicy.MaxAttempts = *spec.TaskTemplate.RestartPolicy.MaxAttempts
}
if spec.TaskTemplate.RestartPolicy.Delay != nil {
if spec.TaskTemplate.RestartPolicy.Delay != nil && *spec.TaskTemplate.RestartPolicy.Delay > 0 {
si.RestartPolicy.Delay = spec.TaskTemplate.RestartPolicy.Delay.String()
}
if spec.TaskTemplate.RestartPolicy.Window != nil {
if spec.TaskTemplate.RestartPolicy.Window != nil && *spec.TaskTemplate.RestartPolicy.Window > 0 {
si.RestartPolicy.Window = spec.TaskTemplate.RestartPolicy.Window.String()
}
}
@ -347,7 +351,9 @@ func NewResourceInfo(res *swarm.Resources) ResourceInfo {
ri := ResourceInfo{}
if res != nil {
ri.CPU = float64(res.NanoCPUs) / 1e9
ri.Memory = size.Size(res.MemoryBytes).String()
if res.MemoryBytes > 0 {
ri.Memory = size.Size(res.MemoryBytes).String()
}
}
return ri
}

View File

@ -75,6 +75,15 @@
</div>
</div>
<div class="block">
<div class="block-header">
<p>Command line</p>
</div>
<div class="block-body is-bordered">
{{ .Command }}
</div>
</div>
{{if .Service.Endpoint.VirtualIPs}}
<div class="block">
<div class="block-header">