feat: implement --pull policy for run command: always, missing, never

This commit is contained in:
Pavel Sviderski
2025-03-27 15:06:32 +10:00
parent 7c7530f977
commit 641b11a92d
3 changed files with 34 additions and 7 deletions
+17 -6
View File
@@ -21,6 +21,7 @@ type runOptions struct {
mode string mode string
name string name string
publish []string publish []string
pull string
replicas uint replicas uint
volumes []string volumes []string
@@ -50,8 +51,8 @@ func NewRunCommand() *cobra.Command {
cmd.Flags().StringVar(&opts.entrypoint, "entrypoint", "", cmd.Flags().StringVar(&opts.entrypoint, "entrypoint", "",
"Overwrite the default ENTRYPOINT of the image. Pass an empty string \"\" to reset it.") "Overwrite the default ENTRYPOINT of the image. Pass an empty string \"\" to reset it.")
cmd.Flags().StringVar(&opts.mode, "mode", api.ServiceModeReplicated, cmd.Flags().StringVar(&opts.mode, "mode", api.ServiceModeReplicated,
fmt.Sprintf("Replication mode of the service: either %q (a specified number of containers across "+ fmt.Sprintf("Replication mode of the service: either '%s' (a specified number of containers across "+
"the machines) or %q (one container on every machine).", "the machines) or '%s' (one container on every machine).",
api.ServiceModeReplicated, api.ServiceModeGlobal)) api.ServiceModeReplicated, api.ServiceModeGlobal))
cmd.Flags().StringSliceVarP(&opts.machines, "machine", "m", nil, cmd.Flags().StringSliceVarP(&opts.machines, "machine", "m", nil,
"Placement constraint by machine name, limiting which machines the service can run on. Can be specified "+ "Placement constraint by machine name, limiting which machines the service can run on. Can be specified "+
@@ -68,6 +69,9 @@ func NewRunCommand() *cobra.Command {
" -p app.example.com:8080/https Publish port 8080 as HTTPS via load balancer with custom hostname\n"+ " -p app.example.com:8080/https Publish port 8080 as HTTPS via load balancer with custom hostname\n"+
" -p 9000:8080 Publish port 8080 as TCP port 9000 via load balancer\n"+ " -p 9000:8080 Publish port 8080 as TCP port 9000 via load balancer\n"+
" -p 53:5353/udp@host Bind UDP port 5353 to host port 53") " -p 53:5353/udp@host Bind UDP port 5353 to host port 53")
cmd.Flags().StringVar(&opts.pull, "pull", api.PullPolicyMissing,
fmt.Sprintf("Pull image from the registry before running service containers ('%s', '%s', '%s').",
api.PullPolicyAlways, api.PullPolicyMissing, api.PullPolicyNever))
cmd.Flags().UintVar(&opts.replicas, "replicas", 1, cmd.Flags().UintVar(&opts.replicas, "replicas", 1,
"Number of containers to run for the service. Only valid for a replicated service.") "Number of containers to run for the service. Only valid for a replicated service.")
cmd.Flags().StringSliceVarP(&opts.volumes, "volume", "v", nil, cmd.Flags().StringSliceVarP(&opts.volumes, "volume", "v", nil,
@@ -86,7 +90,13 @@ func run(ctx context.Context, uncli *cli.CLI, opts runOptions) error {
switch opts.mode { switch opts.mode {
case api.ServiceModeReplicated, api.ServiceModeGlobal: case api.ServiceModeReplicated, api.ServiceModeGlobal:
default: default:
return fmt.Errorf("invalid replication mode: %q", opts.mode) return fmt.Errorf("invalid replication mode: '%s'", opts.mode)
}
switch opts.pull {
case api.PullPolicyAlways, api.PullPolicyMissing, api.PullPolicyNever:
default:
return fmt.Errorf("invalid pull policy: '%s'", opts.pull)
} }
var machineFilter deploy.MachineFilter var machineFilter deploy.MachineFilter
@@ -124,9 +134,10 @@ func run(ctx context.Context, uncli *cli.CLI, opts runOptions) error {
spec := api.ServiceSpec{ spec := api.ServiceSpec{
Container: api.ContainerSpec{ Container: api.ContainerSpec{
Command: opts.command, Command: opts.command,
Image: opts.image, Image: opts.image,
Volumes: opts.volumes, PullPolicy: opts.pull,
Volumes: opts.volumes,
}, },
Mode: opts.mode, Mode: opts.mode,
Name: opts.name, Name: opts.name,
+16 -1
View File
@@ -110,10 +110,25 @@ func (cli *Client) CreateContainer(
pw := progress.ContextWriter(ctx) pw := progress.ContextWriter(ctx)
eventID := fmt.Sprintf("Container %s on %s", containerName, machine.Machine.Name) eventID := fmt.Sprintf("Container %s on %s", containerName, machine.Machine.Name)
pw.Event(progress.CreatingEvent(eventID)) pw.Event(progress.CreatingEvent(eventID))
if spec.Container.PullPolicy == api.PullPolicyAlways {
if err = cli.pullImageWithProgress(ctx, config.Image, machine.Machine.Name, eventID); err != nil {
return resp, err
}
}
resp, err = cli.Docker.CreateContainer(ctx, config, hostConfig, netConfig, nil, containerName) resp, err = cli.Docker.CreateContainer(ctx, config, hostConfig, netConfig, nil, containerName)
if err != nil { if err != nil {
switch spec.Container.PullPolicy {
case api.PullPolicyAlways, api.PullPolicyNever:
return resp, err
case api.PullPolicyMissing:
default:
return resp, fmt.Errorf("unsupported pull policy: '%s'", spec.Container.PullPolicy)
}
// Not found error is expected if the image is missing.
if !dockerclient.IsErrNotFound(err) { if !dockerclient.IsErrNotFound(err) {
return resp, err return resp, err
} }
+1
View File
@@ -48,6 +48,7 @@ func (s *RollingStrategy) Plan(
// planReplicated creates a plan for a replicated service deployment. // planReplicated creates a plan for a replicated service deployment.
// For replicated services, we want to maintain a specific number of containers (replicas) across the available machines // For replicated services, we want to maintain a specific number of containers (replicas) across the available machines
// in the cluster. // in the cluster.
// TODO: schedule containers only on machines that contain the image if pull policy is set to 'never'.
func (s *RollingStrategy) planReplicated( func (s *RollingStrategy) planReplicated(
ctx context.Context, cli api.MachineClient, svc *api.Service, spec api.ServiceSpec, ctx context.Context, cli api.MachineClient, svc *api.Service, spec api.ServiceSpec,
) (Plan, error) { ) (Plan, error) {