mirror of
https://github.com/cuigh/swirl
synced 2024-12-27 22:32:49 +00:00
270 lines
7.7 KiB
Go
270 lines
7.7 KiB
Go
package docker
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"sort"
|
|
"strconv"
|
|
|
|
"github.com/cuigh/swirl/misc"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/docker/docker/api/types/versions"
|
|
"github.com/docker/docker/client"
|
|
"github.com/docker/docker/pkg/stdcopy"
|
|
)
|
|
|
|
// ServiceList return service list.
|
|
func (d *Docker) ServiceList(ctx context.Context, name, mode string, pageIndex, pageSize int) (services []swarm.Service, totalCount int, err error) {
|
|
err = d.call(func(c *client.Client) (err error) {
|
|
args := filters.NewArgs()
|
|
if name != "" {
|
|
args.Add("name", name)
|
|
}
|
|
if mode != "" {
|
|
args.Add("mode", mode)
|
|
}
|
|
services, err = c.ServiceList(ctx, types.ServiceListOptions{Status: true, Filters: args})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sort.Slice(services, func(i, j int) bool {
|
|
return services[i].Spec.Name < services[j].Spec.Name
|
|
})
|
|
totalCount = len(services)
|
|
start, end := misc.Page(totalCount, pageIndex, pageSize)
|
|
services = services[start:end]
|
|
|
|
if versions.LessThan(c.ClientVersion(), "1.41") {
|
|
err = d.fillStatus(ctx, c, services)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
// ServiceInspect return service raw information.
|
|
func (d *Docker) ServiceInspect(ctx context.Context, name string, status bool) (service swarm.Service, raw []byte, err error) {
|
|
var c *client.Client
|
|
if c, err = d.client(); err != nil {
|
|
return
|
|
}
|
|
|
|
service, raw, err = c.ServiceInspectWithRaw(ctx, name, types.ServiceInspectOptions{})
|
|
if err == nil && status {
|
|
var services []swarm.Service
|
|
if versions.LessThan(c.ClientVersion(), "1.41") {
|
|
services = []swarm.Service{service}
|
|
_ = d.fillStatus(ctx, c, services)
|
|
service.ServiceStatus = services[0].ServiceStatus
|
|
} else {
|
|
services, _ = c.ServiceList(ctx, types.ServiceListOptions{
|
|
Status: true,
|
|
Filters: filters.NewArgs(filters.Arg("name", name)),
|
|
})
|
|
if len(services) > 0 {
|
|
service.ServiceStatus = services[0].ServiceStatus
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (d *Docker) fillStatus(ctx context.Context, c *client.Client, services []swarm.Service) (err error) {
|
|
var (
|
|
nodes map[string]*Node
|
|
m = make(map[string]*swarm.Service)
|
|
tasks []swarm.Task
|
|
opts = types.TaskListOptions{Filters: filters.NewArgs()}
|
|
)
|
|
|
|
nodes, err = d.NodeMap()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for i := range services {
|
|
s := &services[i]
|
|
s.ServiceStatus = &swarm.ServiceStatus{}
|
|
if s.Spec.Mode.Replicated != nil {
|
|
s.ServiceStatus.DesiredTasks = *s.Spec.Mode.Replicated.Replicas
|
|
}
|
|
m[s.ID] = s
|
|
opts.Filters.Add("service", s.ID)
|
|
}
|
|
|
|
// acquire all related tasks
|
|
tasks, err = c.TaskList(ctx, opts)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// count tasks
|
|
for _, task := range tasks {
|
|
s := m[task.ServiceID]
|
|
if s != nil {
|
|
if s.Spec.Mode.Global != nil && task.DesiredState != swarm.TaskStateShutdown {
|
|
s.ServiceStatus.DesiredTasks++
|
|
}
|
|
if n, ok := nodes[task.NodeID]; ok && n.State != swarm.NodeStateDown && task.Status.State == swarm.TaskStateRunning {
|
|
s.ServiceStatus.RunningTasks++
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// ServiceRemove remove a service.
|
|
func (d *Docker) ServiceRemove(ctx context.Context, name string) error {
|
|
return d.call(func(c *client.Client) (err error) {
|
|
return c.ServiceRemove(ctx, name)
|
|
})
|
|
}
|
|
|
|
// ServiceRollback rollbacks a service.
|
|
func (d *Docker) ServiceRollback(ctx context.Context, name string) error {
|
|
return d.call(func(c *client.Client) (err error) {
|
|
service, _, err := c.ServiceInspectWithRaw(ctx, name, types.ServiceInspectOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
options := types.ServiceUpdateOptions{
|
|
Rollback: "previous",
|
|
}
|
|
resp, err := c.ServiceUpdate(ctx, name, service.Version, service.Spec, options)
|
|
if err == nil && len(resp.Warnings) > 0 {
|
|
d.logger.Warnf("service '%s' was rollbacked but got warnings: %v", name, resp.Warnings)
|
|
}
|
|
return err
|
|
})
|
|
}
|
|
|
|
// ServiceRestart force to refresh a service.
|
|
func (d *Docker) ServiceRestart(ctx context.Context, name string) error {
|
|
return d.call(func(c *client.Client) (err error) {
|
|
service, _, err := c.ServiceInspectWithRaw(ctx, name, types.ServiceInspectOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
service.Spec.TaskTemplate.ForceUpdate++
|
|
resp, err := c.ServiceUpdate(ctx, name, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
|
if err == nil && len(resp.Warnings) > 0 {
|
|
d.logger.Warnf("service '%s' was restarted but got warnings: %v", name, resp.Warnings)
|
|
}
|
|
return err
|
|
})
|
|
}
|
|
|
|
// ServiceScale adjust replicas of a service.
|
|
func (d *Docker) ServiceScale(ctx context.Context, name string, count, version uint64) error {
|
|
return d.call(func(c *client.Client) (err error) {
|
|
service, _, err := c.ServiceInspectWithRaw(ctx, name, types.ServiceInspectOptions{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
spec := service.Spec
|
|
if spec.Mode.Replicated != nil {
|
|
spec.Mode.Replicated.Replicas = &count
|
|
} else if spec.Mode.ReplicatedJob != nil {
|
|
spec.Mode.ReplicatedJob.TotalCompletions = &count
|
|
} else {
|
|
return errors.New("scale can only be used with replicated or replicated-job mode")
|
|
}
|
|
|
|
ver := service.Version
|
|
if version > 0 {
|
|
ver = swarm.Version{Index: version}
|
|
}
|
|
resp, err := c.ServiceUpdate(ctx, name, ver, spec, types.ServiceUpdateOptions{})
|
|
if err == nil && len(resp.Warnings) > 0 {
|
|
d.logger.Warnf("service %s was scaled but got warnings: %v", name, resp.Warnings)
|
|
}
|
|
return err
|
|
})
|
|
}
|
|
|
|
// ServiceCreate create a service.
|
|
func (d *Docker) ServiceCreate(ctx context.Context, spec *swarm.ServiceSpec, registryAuth string) error {
|
|
return d.call(func(c *client.Client) (err error) {
|
|
opts := types.ServiceCreateOptions{EncodedRegistryAuth: registryAuth}
|
|
var resp types.ServiceCreateResponse
|
|
resp, err = c.ServiceCreate(ctx, *spec, opts)
|
|
if err == nil && len(resp.Warnings) > 0 {
|
|
d.logger.Warnf("service %s was created but got warnings: %v", spec.Name, resp.Warnings)
|
|
}
|
|
return
|
|
})
|
|
}
|
|
|
|
// ServiceUpdate update a service.
|
|
func (d *Docker) ServiceUpdate(ctx context.Context, spec *swarm.ServiceSpec, version uint64) error {
|
|
return d.call(func(c *client.Client) (err error) {
|
|
var (
|
|
resp types.ServiceUpdateResponse
|
|
options = types.ServiceUpdateOptions{
|
|
RegistryAuthFrom: types.RegistryAuthFromSpec,
|
|
QueryRegistry: false,
|
|
}
|
|
)
|
|
resp, err = c.ServiceUpdate(ctx, spec.Name, newVersion(version), *spec, options)
|
|
if err == nil && len(resp.Warnings) > 0 {
|
|
d.logger.Warnf("service %s was updated but got warnings: %v", spec.Name, resp.Warnings)
|
|
}
|
|
return
|
|
})
|
|
}
|
|
|
|
// ServiceCount return number of services.
|
|
func (d *Docker) ServiceCount(ctx context.Context) (count int, err error) {
|
|
err = d.call(func(c *client.Client) (err error) {
|
|
var services []swarm.Service
|
|
if services, err = c.ServiceList(ctx, types.ServiceListOptions{}); err == nil {
|
|
count = len(services)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
// ServiceLogs returns the logs generated by a service.
|
|
func (d *Docker) ServiceLogs(ctx context.Context, name string, lines int, timestamps bool) (stdout, stderr *bytes.Buffer, err error) {
|
|
err = d.call(func(c *client.Client) (err error) {
|
|
var (
|
|
rc io.ReadCloser
|
|
opts = types.ContainerLogsOptions{
|
|
ShowStdout: true,
|
|
ShowStderr: true,
|
|
Tail: strconv.Itoa(lines),
|
|
Timestamps: timestamps,
|
|
//Since: (time.Hour * 24).String()
|
|
}
|
|
)
|
|
if rc, err = c.ServiceLogs(ctx, name, opts); err == nil {
|
|
defer rc.Close()
|
|
|
|
stdout = &bytes.Buffer{}
|
|
stderr = &bytes.Buffer{}
|
|
_, err = stdcopy.StdCopy(stdout, stderr, rc)
|
|
}
|
|
return
|
|
})
|
|
return
|
|
}
|
|
|
|
// ServiceSearch search services with args.
|
|
func (d *Docker) ServiceSearch(ctx context.Context, args filters.Args) (services []swarm.Service, err error) {
|
|
err = d.call(func(c *client.Client) (err error) {
|
|
opts := types.ServiceListOptions{Filters: args}
|
|
services, err = c.ServiceList(ctx, opts)
|
|
return
|
|
})
|
|
return
|
|
}
|