mirror of https://github.com/docker/cli.git
Merge pull request #4258 from gmargaritis/373-support-detach-in-docker-stack-deploy
Add support for --detach flag in stack deploy
This commit is contained in:
commit
4a43b8eaed
|
@ -137,7 +137,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitOnService(ctx, dockerCli, response.ID, opts.quiet)
|
return WaitOnService(ctx, dockerCli, response.ID, opts.quiet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setConfigs does double duty: it both sets the ConfigReferences of the
|
// setConfigs does double duty: it both sets the ConfigReferences of the
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
)
|
)
|
||||||
|
|
||||||
// waitOnService waits for the service to converge. It outputs a progress bar,
|
// WaitOnService waits for the service to converge. It outputs a progress bar,
|
||||||
// if appropriate based on the CLI flags.
|
// if appropriate based on the CLI flags.
|
||||||
func waitOnService(ctx context.Context, dockerCli command.Cli, serviceID string, quiet bool) error {
|
func WaitOnService(ctx context.Context, dockerCli command.Cli, serviceID string, quiet bool) error {
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
pipeReader, pipeWriter := io.Pipe()
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ func ServiceProgress(ctx context.Context, apiClient client.APIClient, serviceID
|
||||||
if converged && time.Since(convergedAt) >= monitor {
|
if converged && time.Since(convergedAt) >= monitor {
|
||||||
progressOut.WriteProgress(progress.Progress{
|
progressOut.WriteProgress(progress.Progress{
|
||||||
ID: "verify",
|
ID: "verify",
|
||||||
Action: "Service converged",
|
Action: fmt.Sprintf("Service %s converged", serviceID),
|
||||||
})
|
})
|
||||||
if message != nil {
|
if message != nil {
|
||||||
progressOut.WriteProgress(*message)
|
progressOut.WriteProgress(*message)
|
||||||
|
|
|
@ -62,5 +62,5 @@ func runRollback(ctx context.Context, dockerCli command.Cli, options *serviceOpt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
|
return WaitOnService(ctx, dockerCli, serviceID, options.quiet)
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ func runScale(ctx context.Context, dockerCli command.Cli, options *scaleOptions,
|
||||||
if len(serviceIDs) > 0 {
|
if len(serviceIDs) > 0 {
|
||||||
if !options.detach && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.29") {
|
if !options.detach && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.29") {
|
||||||
for _, serviceID := range serviceIDs {
|
for _, serviceID := range serviceIDs {
|
||||||
if err := waitOnService(ctx, dockerCli, serviceID, false); err != nil {
|
if err := WaitOnService(ctx, dockerCli, serviceID, false); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
|
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
|
return WaitOnService(ctx, dockerCli, serviceID, options.quiet)
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
|
|
|
@ -26,7 +26,7 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return swarm.RunDeploy(cmd.Context(), dockerCli, opts, config)
|
return swarm.RunDeploy(cmd.Context(), dockerCli, cmd.Flags(), &opts, config)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||||
|
@ -42,5 +42,7 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
flags.StringVar(&opts.ResolveImage, "resolve-image", swarm.ResolveImageAlways,
|
flags.StringVar(&opts.ResolveImage, "resolve-image", swarm.ResolveImageAlways,
|
||||||
`Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`", "`+swarm.ResolveImageChanged+`", "`+swarm.ResolveImageNever+`")`)
|
`Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`", "`+swarm.ResolveImageChanged+`", "`+swarm.ResolveImageNever+`")`)
|
||||||
flags.SetAnnotation("resolve-image", "version", []string{"1.30"})
|
flags.SetAnnotation("resolve-image", "version", []string{"1.30"})
|
||||||
|
flags.BoolVarP(&opts.Detach, "detach", "d", true, "Exit immediately instead of waiting for the stack services to converge")
|
||||||
|
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Suppress progress output")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ type Deploy struct {
|
||||||
ResolveImage string
|
ResolveImage string
|
||||||
SendRegistryAuth bool
|
SendRegistryAuth bool
|
||||||
Prune bool
|
Prune bool
|
||||||
|
Detach bool
|
||||||
|
Quiet bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config holds docker stack config options
|
// Config holds docker stack config options
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Resolve image constants
|
// Resolve image constants
|
||||||
|
@ -22,8 +23,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunDeploy is the swarm implementation of docker stack deploy
|
// RunDeploy is the swarm implementation of docker stack deploy
|
||||||
func RunDeploy(ctx context.Context, dockerCli command.Cli, opts options.Deploy, cfg *composetypes.Config) error {
|
func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, opts *options.Deploy, cfg *composetypes.Config) error {
|
||||||
if err := validateResolveImageFlag(&opts); err != nil {
|
if err := validateResolveImageFlag(opts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// client side image resolution should not be done when the supported
|
// client side image resolution should not be done when the supported
|
||||||
|
@ -32,6 +33,11 @@ func RunDeploy(ctx context.Context, dockerCli command.Cli, opts options.Deploy,
|
||||||
opts.ResolveImage = ResolveImageNever
|
opts.ResolveImage = ResolveImageNever
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Detach && !flags.Changed("detach") {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), "Since --detach=false was not specified, tasks will be created in the background.\n"+
|
||||||
|
"In a future release, --detach=false will become the default.")
|
||||||
|
}
|
||||||
|
|
||||||
return deployCompose(ctx, dockerCli, opts, cfg)
|
return deployCompose(ctx, dockerCli, opts, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,11 @@ package swarm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
servicecli "github.com/docker/cli/cli/command/service"
|
||||||
"github.com/docker/cli/cli/command/stack/options"
|
"github.com/docker/cli/cli/command/stack/options"
|
||||||
"github.com/docker/cli/cli/compose/convert"
|
"github.com/docker/cli/cli/compose/convert"
|
||||||
composetypes "github.com/docker/cli/cli/compose/types"
|
composetypes "github.com/docker/cli/cli/compose/types"
|
||||||
|
@ -13,10 +15,9 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
apiclient "github.com/docker/docker/client"
|
apiclient "github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Deploy, config *composetypes.Config) error {
|
func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Deploy, config *composetypes.Config) error {
|
||||||
if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil {
|
if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -60,7 +61,17 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Depl
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return deployServices(ctx, dockerCli, services, namespace, opts.SendRegistryAuth, opts.ResolveImage)
|
|
||||||
|
serviceIDs, err := deployServices(ctx, dockerCli, services, namespace, opts.SendRegistryAuth, opts.ResolveImage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Detach {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitOnServices(ctx, dockerCli, serviceIDs, opts.Quiet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} {
|
func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} {
|
||||||
|
@ -87,11 +98,11 @@ func validateExternalNetworks(ctx context.Context, client apiclient.NetworkAPICl
|
||||||
network, err := client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
|
network, err := client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
|
||||||
switch {
|
switch {
|
||||||
case errdefs.IsNotFound(err):
|
case errdefs.IsNotFound(err):
|
||||||
return errors.Errorf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)
|
return fmt.Errorf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
case network.Scope != "swarm":
|
case network.Scope != "swarm":
|
||||||
return errors.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, network.Scope)
|
return fmt.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, network.Scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -106,13 +117,13 @@ func createSecrets(ctx context.Context, dockerCli command.Cli, secrets []swarm.S
|
||||||
case err == nil:
|
case err == nil:
|
||||||
// secret already exists, then we update that
|
// secret already exists, then we update that
|
||||||
if err := client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec); err != nil {
|
if err := client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec); err != nil {
|
||||||
return errors.Wrapf(err, "failed to update secret %s", secretSpec.Name)
|
return fmt.Errorf("failed to update secret %s: %w", secretSpec.Name, err)
|
||||||
}
|
}
|
||||||
case errdefs.IsNotFound(err):
|
case errdefs.IsNotFound(err):
|
||||||
// secret does not exist, then we create a new one.
|
// secret does not exist, then we create a new one.
|
||||||
fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
|
fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
|
||||||
if _, err := client.SecretCreate(ctx, secretSpec); err != nil {
|
if _, err := client.SecretCreate(ctx, secretSpec); err != nil {
|
||||||
return errors.Wrapf(err, "failed to create secret %s", secretSpec.Name)
|
return fmt.Errorf("failed to create secret %s: %w", secretSpec.Name, err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
|
@ -130,13 +141,13 @@ func createConfigs(ctx context.Context, dockerCli command.Cli, configs []swarm.C
|
||||||
case err == nil:
|
case err == nil:
|
||||||
// config already exists, then we update that
|
// config already exists, then we update that
|
||||||
if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil {
|
if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil {
|
||||||
return errors.Wrapf(err, "failed to update config %s", configSpec.Name)
|
return fmt.Errorf("failed to update config %s: %w", configSpec.Name, err)
|
||||||
}
|
}
|
||||||
case errdefs.IsNotFound(err):
|
case errdefs.IsNotFound(err):
|
||||||
// config does not exist, then we create a new one.
|
// config does not exist, then we create a new one.
|
||||||
fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name)
|
fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name)
|
||||||
if _, err := client.ConfigCreate(ctx, configSpec); err != nil {
|
if _, err := client.ConfigCreate(ctx, configSpec); err != nil {
|
||||||
return errors.Wrapf(err, "failed to create config %s", configSpec.Name)
|
return fmt.Errorf("failed to create config %s: %w", configSpec.Name, err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return err
|
return err
|
||||||
|
@ -169,19 +180,19 @@ func createNetworks(ctx context.Context, dockerCli command.Cli, namespace conver
|
||||||
|
|
||||||
fmt.Fprintf(dockerCli.Out(), "Creating network %s\n", name)
|
fmt.Fprintf(dockerCli.Out(), "Creating network %s\n", name)
|
||||||
if _, err := client.NetworkCreate(ctx, name, createOpts); err != nil {
|
if _, err := client.NetworkCreate(ctx, name, createOpts); err != nil {
|
||||||
return errors.Wrapf(err, "failed to create network %s", name)
|
return fmt.Errorf("failed to create network %s: %w", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) error {
|
func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) ([]string, error) {
|
||||||
apiClient := dockerCli.Client()
|
apiClient := dockerCli.Client()
|
||||||
out := dockerCli.Out()
|
out := dockerCli.Out()
|
||||||
|
|
||||||
existingServices, err := getStackServices(ctx, apiClient, namespace.Name())
|
existingServices, err := getStackServices(ctx, apiClient, namespace.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
existingServiceMap := make(map[string]swarm.Service)
|
existingServiceMap := make(map[string]swarm.Service)
|
||||||
|
@ -189,6 +200,8 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
|
||||||
existingServiceMap[service.Spec.Name] = service
|
existingServiceMap[service.Spec.Name] = service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var serviceIDs []string
|
||||||
|
|
||||||
for internalName, serviceSpec := range services {
|
for internalName, serviceSpec := range services {
|
||||||
var (
|
var (
|
||||||
name = namespace.Scope(internalName)
|
name = namespace.Scope(internalName)
|
||||||
|
@ -200,7 +213,7 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
|
||||||
// Retrieve encoded auth token from the image reference
|
// Retrieve encoded auth token from the image reference
|
||||||
encodedAuth, err = command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
|
encodedAuth, err = command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,12 +254,14 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
|
||||||
|
|
||||||
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, serviceSpec, updateOpts)
|
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, serviceSpec, updateOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to update service %s", name)
|
return nil, fmt.Errorf("failed to update service %s: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, warning := range response.Warnings {
|
for _, warning := range response.Warnings {
|
||||||
fmt.Fprintln(dockerCli.Err(), warning)
|
fmt.Fprintln(dockerCli.Err(), warning)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceIDs = append(serviceIDs, service.ID)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(out, "Creating service %s\n", name)
|
fmt.Fprintf(out, "Creating service %s\n", name)
|
||||||
|
|
||||||
|
@ -257,10 +272,29 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
|
||||||
createOpts.QueryRegistry = true
|
createOpts.QueryRegistry = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts); err != nil {
|
response, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts)
|
||||||
return errors.Wrapf(err, "failed to create service %s", name)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create service %s: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceIDs = append(serviceIDs, response.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return serviceIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitOnServices(ctx context.Context, dockerCli command.Cli, serviceIDs []string, quiet bool) error {
|
||||||
|
var errs []error
|
||||||
|
for _, serviceID := range serviceIDs {
|
||||||
|
if err := servicecli.WaitOnService(ctx, dockerCli, serviceID, quiet); err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("%s: %w", serviceID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func TestServiceUpdateResolveImageChanged(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := deployServices(ctx, client, spec, namespace, false, ResolveImageChanged)
|
_, err := deployServices(ctx, client, spec, namespace, false, ResolveImageChanged)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(receivedOptions.QueryRegistry, tc.expectedQueryRegistry))
|
assert.Check(t, is.Equal(receivedOptions.QueryRegistry, tc.expectedQueryRegistry))
|
||||||
assert.Check(t, is.Equal(receivedService.TaskTemplate.ContainerSpec.Image, tc.expectedImage))
|
assert.Check(t, is.Equal(receivedService.TaskTemplate.ContainerSpec.Image, tc.expectedImage))
|
||||||
|
|
|
@ -12,7 +12,9 @@ Deploy a new stack or update an existing stack
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:---------------------------------------------------------|:--------------|:---------|:--------------------------------------------------------------------------------------------------|
|
|:---------------------------------------------------------|:--------------|:---------|:--------------------------------------------------------------------------------------------------|
|
||||||
| [`-c`](#compose-file), [`--compose-file`](#compose-file) | `stringSlice` | | Path to a Compose file, or `-` to read from stdin |
|
| [`-c`](#compose-file), [`--compose-file`](#compose-file) | `stringSlice` | | Path to a Compose file, or `-` to read from stdin |
|
||||||
|
| `-d`, `--detach` | `bool` | `true` | Exit immediately instead of waiting for the stack services to converge |
|
||||||
| `--prune` | | | Prune services that are no longer referenced |
|
| `--prune` | | | Prune services that are no longer referenced |
|
||||||
|
| `-q`, `--quiet` | | | Suppress progress output |
|
||||||
| `--resolve-image` | `string` | `always` | Query the registry to resolve image digest and supported platforms (`always`, `changed`, `never`) |
|
| `--resolve-image` | `string` | `always` | Query the registry to resolve image digest and supported platforms (`always`, `changed`, `never`) |
|
||||||
| `--with-registry-auth` | | | Send registry authentication details to Swarm agents |
|
| `--with-registry-auth` | | | Send registry authentication details to Swarm agents |
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,10 @@ Aliases:
|
||||||
Options:
|
Options:
|
||||||
-c, --compose-file strings Path to a Compose file, or "-" to read
|
-c, --compose-file strings Path to a Compose file, or "-" to read
|
||||||
from stdin
|
from stdin
|
||||||
|
-d, --detach Exit immediately instead of waiting for
|
||||||
|
the stack services to converge (default true)
|
||||||
--prune Prune services that are no longer referenced
|
--prune Prune services that are no longer referenced
|
||||||
|
-q, --quiet Suppress progress output
|
||||||
--resolve-image string Query the registry to resolve image digest
|
--resolve-image string Query the registry to resolve image digest
|
||||||
and supported platforms ("always",
|
and supported platforms ("always",
|
||||||
"changed", "never") (default "always")
|
"changed", "never") (default "always")
|
||||||
|
|
Loading…
Reference in New Issue