53 lines
1.9 KiB
Go
53 lines
1.9 KiB
Go
package updater
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/docker/docker/api/types/swarm"
|
|
"github.com/shcizo/package-updater/internal/discovery"
|
|
)
|
|
|
|
// SwarmDockerClient is the subset of the Docker SDK SwarmExecutor depends
|
|
// on. Defined as an interface so tests can supply a fake.
|
|
type SwarmDockerClient interface {
|
|
ServiceInspectWithRaw(ctx context.Context, serviceID string, opts swarm.ServiceInspectOptions) (swarm.Service, []byte, error)
|
|
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, opts swarm.ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error)
|
|
}
|
|
|
|
// SwarmExecutor updates a Swarm service's image via the Docker API,
|
|
// equivalent to `docker service update --image`.
|
|
type SwarmExecutor struct {
|
|
cli SwarmDockerClient
|
|
}
|
|
|
|
// NewSwarmExecutor returns an executor that drives Swarm service updates
|
|
// through the Docker API.
|
|
func NewSwarmExecutor(cli SwarmDockerClient) *SwarmExecutor {
|
|
return &SwarmExecutor{cli: cli}
|
|
}
|
|
|
|
// Execute inspects the service to get its current spec and version (required
|
|
// by the Docker API as an optimistic-concurrency token), sets the new image
|
|
// on the container spec, and calls ServiceUpdate. QueryRegistry is set so a
|
|
// floating tag (e.g. ":latest") resolves to a fresh digest and actually
|
|
// triggers a rolling update instead of being treated as unchanged.
|
|
func (e *SwarmExecutor) Execute(ctx context.Context, job discovery.Job) error {
|
|
if job.Refused {
|
|
return fmt.Errorf("refused: %s", job.RefusedReason)
|
|
}
|
|
|
|
svc, _, err := e.cli.ServiceInspectWithRaw(ctx, job.ServiceID, swarm.ServiceInspectOptions{})
|
|
if err != nil {
|
|
return fmt.Errorf("inspect service %s: %w", job.ServiceID, err)
|
|
}
|
|
|
|
spec := svc.Spec
|
|
spec.TaskTemplate.ContainerSpec.Image = job.Image
|
|
|
|
if _, err := e.cli.ServiceUpdate(ctx, job.ServiceID, svc.Version, spec, swarm.ServiceUpdateOptions{QueryRegistry: true}); err != nil {
|
|
return fmt.Errorf("update service %s: %w", job.ServiceID, err)
|
|
}
|
|
return nil
|
|
}
|