mirror of https://github.com/docker/cli.git
Merge pull request #121 from nishanttotla/digest-pinning-stack-deploy
Enable client side digest pinning for stack deploy
This commit is contained in:
commit
4d980880f3
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/compose/convert"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -14,12 +15,16 @@ import (
|
|||
|
||||
const (
|
||||
defaultNetworkDriver = "overlay"
|
||||
resolveImageAlways = "always"
|
||||
resolveImageChanged = "changed"
|
||||
resolveImageNever = "never"
|
||||
)
|
||||
|
||||
type deployOptions struct {
|
||||
bundlefile string
|
||||
composefile string
|
||||
namespace string
|
||||
resolveImage string
|
||||
sendRegistryAuth bool
|
||||
prune bool
|
||||
}
|
||||
|
@ -44,12 +49,19 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
|
|||
addRegistryAuthFlag(&opts.sendRegistryAuth, flags)
|
||||
flags.BoolVar(&opts.prune, "prune", false, "Prune services that are no longer referenced")
|
||||
flags.SetAnnotation("prune", "version", []string{"1.27"})
|
||||
flags.StringVar(&opts.resolveImage, "resolve-image", resolveImageAlways,
|
||||
`Query the registry to resolve image digest and supported platforms ("`+resolveImageAlways+`"|"`+resolveImageChanged+`"|"`+resolveImageNever+`")`)
|
||||
flags.SetAnnotation("resolve-image", "version", []string{"1.30"})
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDeploy(dockerCli command.Cli, opts deployOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
if err := validateResolveImageFlag(dockerCli, &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case opts.bundlefile == "" && opts.composefile == "":
|
||||
return errors.Errorf("Please specify either a bundle file (with --bundle-file) or a Compose file (with --compose-file).")
|
||||
|
@ -62,6 +74,20 @@ func runDeploy(dockerCli command.Cli, opts deployOptions) error {
|
|||
}
|
||||
}
|
||||
|
||||
// validateResolveImageFlag validates the opts.resolveImage command line option
|
||||
// and also turns image resolution off if the version is older than 1.30
|
||||
func validateResolveImageFlag(dockerCli command.Cli, opts *deployOptions) error {
|
||||
if opts.resolveImage != resolveImageAlways && opts.resolveImage != resolveImageChanged && opts.resolveImage != resolveImageNever {
|
||||
return errors.Errorf("Invalid option %s for flag --resolve-image", opts.resolveImage)
|
||||
}
|
||||
// client side image resolution should not be done when the supported
|
||||
// server version is older than 1.30
|
||||
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") {
|
||||
opts.resolveImage = resolveImageNever
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkDaemonIsSwarmManager does an Info API call to verify that the daemon is
|
||||
// a swarm manager. This is necessary because we must create networks before we
|
||||
// create services, but the API call for creating a network does not return a
|
||||
|
|
|
@ -87,5 +87,5 @@ func deployBundle(ctx context.Context, dockerCli command.Cli, opts deployOptions
|
|||
if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil {
|
||||
return err
|
||||
}
|
||||
return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth)
|
||||
return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth, opts.resolveImage)
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts deployOption
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth)
|
||||
return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth, opts.resolveImage)
|
||||
}
|
||||
|
||||
func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} {
|
||||
|
@ -283,6 +283,7 @@ func deployServices(
|
|||
services map[string]swarm.ServiceSpec,
|
||||
namespace convert.Namespace,
|
||||
sendAuth bool,
|
||||
resolveImage string,
|
||||
) error {
|
||||
apiClient := dockerCli.Client()
|
||||
out := dockerCli.Out()
|
||||
|
@ -301,9 +302,9 @@ func deployServices(
|
|||
name := namespace.Scope(internalName)
|
||||
|
||||
encodedAuth := ""
|
||||
image := serviceSpec.TaskTemplate.ContainerSpec.Image
|
||||
if sendAuth {
|
||||
// Retrieve encoded auth token from the image reference
|
||||
image := serviceSpec.TaskTemplate.ContainerSpec.Image
|
||||
encodedAuth, err = command.RetrieveAuthTokenFromImage(ctx, dockerCli, image)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -313,12 +314,18 @@ func deployServices(
|
|||
if service, exists := existingServiceMap[name]; exists {
|
||||
fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID)
|
||||
|
||||
updateOpts := types.ServiceUpdateOptions{EncodedRegistryAuth: encodedAuth}
|
||||
|
||||
if resolveImage == resolveImageAlways || (resolveImage == resolveImageChanged && image != service.Spec.Labels[convert.LabelImage]) {
|
||||
updateOpts.QueryRegistry = true
|
||||
}
|
||||
|
||||
response, err := apiClient.ServiceUpdate(
|
||||
ctx,
|
||||
service.ID,
|
||||
service.Version,
|
||||
serviceSpec,
|
||||
types.ServiceUpdateOptions{EncodedRegistryAuth: encodedAuth},
|
||||
updateOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to update service %s", name)
|
||||
|
@ -331,6 +338,12 @@ func deployServices(
|
|||
fmt.Fprintf(out, "Creating service %s\n", name)
|
||||
|
||||
createOpts := types.ServiceCreateOptions{EncodedRegistryAuth: encodedAuth}
|
||||
|
||||
// query registry if flag disabling it was not set
|
||||
if resolveImage == resolveImageAlways || resolveImage == resolveImageChanged {
|
||||
createOpts.QueryRegistry = true
|
||||
}
|
||||
|
||||
if _, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts); err != nil {
|
||||
return errors.Wrapf(err, "failed to create service %s", name)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,11 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const defaultNetwork = "default"
|
||||
const (
|
||||
defaultNetwork = "default"
|
||||
// LabelImage is the label used to store image name provided in the compose file
|
||||
LabelImage = "com.docker.stack.image"
|
||||
)
|
||||
|
||||
// Services from compose-file types to engine API types
|
||||
func Services(
|
||||
|
@ -158,6 +162,9 @@ func Service(
|
|||
UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig),
|
||||
}
|
||||
|
||||
// add an image label to serviceSpec
|
||||
serviceSpec.Labels[LabelImage] = service.Image
|
||||
|
||||
// ServiceSpec.Networks is deprecated and should not have been used by
|
||||
// this package. It is possible to update TaskTemplate.Networks, but it
|
||||
// is not possible to update ServiceSpec.Networks. Unfortunately, we
|
||||
|
|
Loading…
Reference in New Issue