Enable client side digest pinning for stack deploy

Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
This commit is contained in:
Nishant Totla 2017-05-22 14:06:36 -07:00
parent 9d12d6fc87
commit 9f1bea2657
No known key found for this signature in database
GPG Key ID: 7EA5781C9B3D0C19
4 changed files with 34 additions and 4 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/compose/convert" "github.com/docker/cli/cli/compose/convert"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/versions"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -21,6 +22,7 @@ type deployOptions struct {
composefile string composefile string
namespace string namespace string
sendRegistryAuth bool sendRegistryAuth bool
noResolveImage bool
prune bool prune bool
} }
@ -44,12 +46,19 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
addRegistryAuthFlag(&opts.sendRegistryAuth, flags) addRegistryAuthFlag(&opts.sendRegistryAuth, flags)
flags.BoolVar(&opts.prune, "prune", false, "Prune services that are no longer referenced") flags.BoolVar(&opts.prune, "prune", false, "Prune services that are no longer referenced")
flags.SetAnnotation("prune", "version", []string{"1.27"}) flags.SetAnnotation("prune", "version", []string{"1.27"})
flags.BoolVar(&opts.noResolveImage, "no-resolve-image", false, "Do not query the registry to resolve image digest and supported platforms")
flags.SetAnnotation("no-resolve-image", "version", []string{"1.30"})
return cmd return cmd
} }
func runDeploy(dockerCli command.Cli, opts deployOptions) error { func runDeploy(dockerCli command.Cli, opts deployOptions) error {
ctx := context.Background() ctx := context.Background()
// image resolution should not happen for clients older than v1.30
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") {
opts.noResolveImage = true
}
switch { switch {
case opts.bundlefile == "" && opts.composefile == "": case opts.bundlefile == "" && opts.composefile == "":
return errors.Errorf("Please specify either a bundle file (with --bundle-file) or a Compose file (with --compose-file).") return errors.Errorf("Please specify either a bundle file (with --bundle-file) or a Compose file (with --compose-file).")

View File

@ -87,5 +87,5 @@ func deployBundle(ctx context.Context, dockerCli command.Cli, opts deployOptions
if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil { if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil {
return err return err
} }
return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth) return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth, opts.noResolveImage)
} }

View File

@ -92,7 +92,7 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts deployOption
if err != nil { if err != nil {
return err return err
} }
return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth) return deployServices(ctx, dockerCli, services, namespace, opts.sendRegistryAuth, opts.noResolveImage)
} }
func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} { func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} {
@ -283,6 +283,7 @@ func deployServices(
services map[string]swarm.ServiceSpec, services map[string]swarm.ServiceSpec,
namespace convert.Namespace, namespace convert.Namespace,
sendAuth bool, sendAuth bool,
noResolveImage bool,
) error { ) error {
apiClient := dockerCli.Client() apiClient := dockerCli.Client()
out := dockerCli.Out() out := dockerCli.Out()
@ -301,9 +302,9 @@ func deployServices(
name := namespace.Scope(internalName) name := namespace.Scope(internalName)
encodedAuth := "" encodedAuth := ""
image := serviceSpec.TaskTemplate.ContainerSpec.Image
if sendAuth { if sendAuth {
// Retrieve encoded auth token from the image reference // Retrieve encoded auth token from the image reference
image := serviceSpec.TaskTemplate.ContainerSpec.Image
encodedAuth, err = command.RetrieveAuthTokenFromImage(ctx, dockerCli, image) encodedAuth, err = command.RetrieveAuthTokenFromImage(ctx, dockerCli, image)
if err != nil { if err != nil {
return err return err
@ -313,12 +314,20 @@ func deployServices(
if service, exists := existingServiceMap[name]; exists { if service, exists := existingServiceMap[name]; exists {
fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID) fmt.Fprintf(out, "Updating service %s (id: %s)\n", name, service.ID)
updateOpts := types.ServiceUpdateOptions{EncodedRegistryAuth: encodedAuth}
if image != service.Spec.Labels["com.docker.stack.image"] {
if !noResolveImage {
updateOpts.QueryRegistry = true
}
}
response, err := apiClient.ServiceUpdate( response, err := apiClient.ServiceUpdate(
ctx, ctx,
service.ID, service.ID,
service.Version, service.Version,
serviceSpec, serviceSpec,
types.ServiceUpdateOptions{EncodedRegistryAuth: encodedAuth}, updateOpts,
) )
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to update service %s", name) return errors.Wrapf(err, "failed to update service %s", name)
@ -331,6 +340,12 @@ func deployServices(
fmt.Fprintf(out, "Creating service %s\n", name) fmt.Fprintf(out, "Creating service %s\n", name)
createOpts := types.ServiceCreateOptions{EncodedRegistryAuth: encodedAuth} createOpts := types.ServiceCreateOptions{EncodedRegistryAuth: encodedAuth}
// query registry if flag disabling it was not set
if !noResolveImage {
createOpts.QueryRegistry = true
}
if _, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts); err != nil { if _, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts); err != nil {
return errors.Wrapf(err, "failed to create service %s", name) return errors.Wrapf(err, "failed to create service %s", name)
} }

View File

@ -45,6 +45,12 @@ func Services(
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "service %s", service.Name) return nil, errors.Wrapf(err, "service %s", service.Name)
} }
// add an image label to serviceSpec
if serviceSpec.Labels == nil {
serviceSpec.Labels = make(map[string]string)
}
serviceSpec.Labels["com.docker.stack.image"] = service.Image
result[service.Name] = serviceSpec result[service.Name] = serviceSpec
} }