Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
(cherry picked from commit 538bac39d7fe93562922b89e9a294096be48fb59)
| ... | ... |
@@ -149,8 +149,8 @@ func (cli *DockerCli) ConfigureAuth(flUser, flPassword, serverAddress string, is |
| 149 | 149 |
return authconfig, nil |
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
-// ResolveAuthConfigFromImage retrieves that AuthConfig using the image string |
|
| 153 |
-func (cli *DockerCli) ResolveAuthConfigFromImage(ctx context.Context, image string) (types.AuthConfig, error) {
|
|
| 152 |
+// resolveAuthConfigFromImage retrieves that AuthConfig using the image string |
|
| 153 |
+func (cli *DockerCli) resolveAuthConfigFromImage(ctx context.Context, image string) (types.AuthConfig, error) {
|
|
| 154 | 154 |
registryRef, err := reference.ParseNamed(image) |
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
return types.AuthConfig{}, err
|
| ... | ... |
@@ -166,7 +166,7 @@ func (cli *DockerCli) ResolveAuthConfigFromImage(ctx context.Context, image stri |
| 166 | 166 |
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image |
| 167 | 167 |
func (cli *DockerCli) RetrieveAuthTokenFromImage(ctx context.Context, image string) (string, error) {
|
| 168 | 168 |
// Retrieve encoded auth token from the image reference |
| 169 |
- authConfig, err := cli.ResolveAuthConfigFromImage(ctx, image) |
|
| 169 |
+ authConfig, err := cli.resolveAuthConfigFromImage(ctx, image) |
|
| 170 | 170 |
if err != nil {
|
| 171 | 171 |
return "", err |
| 172 | 172 |
} |
| ... | ... |
@@ -33,6 +33,7 @@ func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
|
| 33 | 33 |
|
| 34 | 34 |
func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error {
|
| 35 | 35 |
apiClient := dockerCli.Client() |
| 36 |
+ headers := map[string][]string{}
|
|
| 36 | 37 |
|
| 37 | 38 |
service, err := opts.ToService() |
| 38 | 39 |
if err != nil {
|
| ... | ... |
@@ -40,14 +41,15 @@ func runCreate(dockerCli *client.DockerCli, opts *serviceOptions) error {
|
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 | 42 |
ctx := context.Background() |
| 43 |
- // Retrieve encoded auth token from the image reference |
|
| 44 |
- encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, opts.image) |
|
| 45 |
- if err != nil {
|
|
| 46 |
- return err |
|
| 47 |
- } |
|
| 48 | 43 |
|
| 49 |
- headers := map[string][]string{
|
|
| 50 |
- "X-Registry-Auth": {encodedAuth},
|
|
| 44 |
+ // only send auth if flag was set |
|
| 45 |
+ if opts.registryAuth {
|
|
| 46 |
+ // Retrieve encoded auth token from the image reference |
|
| 47 |
+ encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, opts.image) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ headers["X-Registry-Auth"] = []string{encodedAuth}
|
|
| 51 | 52 |
} |
| 52 | 53 |
|
| 53 | 54 |
response, err := apiClient.ServiceCreate(ctx, service, headers) |
| ... | ... |
@@ -373,6 +373,8 @@ type serviceOptions struct {
|
| 373 | 373 |
update updateOptions |
| 374 | 374 |
networks []string |
| 375 | 375 |
endpoint endpointOptions |
| 376 |
+ |
|
| 377 |
+ registryAuth bool |
|
| 376 | 378 |
} |
| 377 | 379 |
|
| 378 | 380 |
func newServiceOptions() *serviceOptions {
|
| ... | ... |
@@ -436,7 +438,7 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
|
| 436 | 436 |
return service, nil |
| 437 | 437 |
} |
| 438 | 438 |
|
| 439 |
-// addServiceFlags adds all flags that are common to both `create` and `update. |
|
| 439 |
+// addServiceFlags adds all flags that are common to both `create` and `update`. |
|
| 440 | 440 |
// Any flags that are not common are added separately in the individual command |
| 441 | 441 |
func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
|
| 442 | 442 |
flags := cmd.Flags() |
| ... | ... |
@@ -469,6 +471,8 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
|
| 469 | 469 |
flags.StringSliceVar(&opts.networks, flagNetwork, []string{}, "Network attachments")
|
| 470 | 470 |
flags.StringVar(&opts.endpoint.mode, flagEndpointMode, "", "Endpoint mode(Valid values: vip, dnsrr)") |
| 471 | 471 |
flags.VarP(&opts.endpoint.ports, flagPublish, "p", "Publish a port as a node port") |
| 472 |
+ |
|
| 473 |
+ flags.BoolVar(&opts.registryAuth, flagRegistryAuth, false, "Send registry authentication details to Swarm agents") |
|
| 472 | 474 |
} |
| 473 | 475 |
|
| 474 | 476 |
const ( |
| ... | ... |
@@ -493,4 +497,5 @@ const ( |
| 493 | 493 |
flagUpdateDelay = "update-delay" |
| 494 | 494 |
flagUpdateParallelism = "update-parallelism" |
| 495 | 495 |
flagUser = "user" |
| 496 |
+ flagRegistryAuth = "registry-auth" |
|
| 496 | 497 |
) |
| ... | ... |
@@ -60,7 +60,6 @@ func runScale(dockerCli *client.DockerCli, args []string) error {
|
| 60 | 60 |
func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string) error {
|
| 61 | 61 |
client := dockerCli.Client() |
| 62 | 62 |
ctx := context.Background() |
| 63 |
- headers := map[string][]string{}
|
|
| 64 | 63 |
|
| 65 | 64 |
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID) |
| 66 | 65 |
|
| ... | ... |
@@ -68,17 +67,6 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string |
| 68 | 68 |
return err |
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 |
- // TODO(nishanttotla): Is this the best way to get the image? |
|
| 72 |
- image := service.Spec.TaskTemplate.ContainerSpec.Image |
|
| 73 |
- if image != "" {
|
|
| 74 |
- // Retrieve encoded auth token from the image reference |
|
| 75 |
- encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) |
|
| 76 |
- if err != nil {
|
|
| 77 |
- return err |
|
| 78 |
- } |
|
| 79 |
- headers["X-Registry-Auth"] = []string{encodedAuth}
|
|
| 80 |
- } |
|
| 81 |
- |
|
| 82 | 71 |
serviceMode := &service.Spec.Mode |
| 83 | 72 |
if serviceMode.Replicated == nil {
|
| 84 | 73 |
return fmt.Errorf("scale can only be used with replicated mode")
|
| ... | ... |
@@ -89,7 +77,7 @@ func runServiceScale(dockerCli *client.DockerCli, serviceID string, scale string |
| 89 | 89 |
} |
| 90 | 90 |
serviceMode.Replicated.Replicas = &uintScale |
| 91 | 91 |
|
| 92 |
- err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers) |
|
| 92 |
+ err = client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, nil) |
|
| 93 | 93 |
if err != nil {
|
| 94 | 94 |
return err |
| 95 | 95 |
} |
| ... | ... |
@@ -41,30 +41,31 @@ func runUpdate(dockerCli *client.DockerCli, flags *pflag.FlagSet, serviceID stri |
| 41 | 41 |
ctx := context.Background() |
| 42 | 42 |
headers := map[string][]string{}
|
| 43 | 43 |
|
| 44 |
- // TODO(nishanttotla): Is this the best way to get the new image? |
|
| 45 |
- image, err := flags.GetString("image")
|
|
| 44 |
+ service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID) |
|
| 46 | 45 |
if err != nil {
|
| 47 | 46 |
return err |
| 48 | 47 |
} |
| 49 |
- if image != "" {
|
|
| 50 |
- // Retrieve encoded auth token from the image reference |
|
| 51 |
- // only do this if a new image has been provided as part of the udpate |
|
| 52 |
- encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) |
|
| 53 |
- if err != nil {
|
|
| 54 |
- return err |
|
| 55 |
- } |
|
| 56 |
- headers["X-Registry-Auth"] = []string{encodedAuth}
|
|
| 57 |
- } |
|
| 58 | 48 |
|
| 59 |
- service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID) |
|
| 49 |
+ err = updateService(flags, &service.Spec) |
|
| 60 | 50 |
if err != nil {
|
| 61 | 51 |
return err |
| 62 | 52 |
} |
| 63 | 53 |
|
| 64 |
- err = updateService(flags, &service.Spec) |
|
| 54 |
+ // only send auth if flag was set |
|
| 55 |
+ sendAuth, err := flags.GetBool(flagRegistryAuth) |
|
| 65 | 56 |
if err != nil {
|
| 66 | 57 |
return err |
| 67 | 58 |
} |
| 59 |
+ if sendAuth {
|
|
| 60 |
+ // Retrieve encoded auth token from the image reference |
|
| 61 |
+ // This would be the old image if it didn't change in this update |
|
| 62 |
+ image := service.Spec.TaskTemplate.ContainerSpec.Image |
|
| 63 |
+ encodedAuth, err := dockerCli.RetrieveAuthTokenFromImage(ctx, image) |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ return err |
|
| 66 |
+ } |
|
| 67 |
+ headers["X-Registry-Auth"] = []string{encodedAuth}
|
|
| 68 |
+ } |
|
| 68 | 69 |
|
| 69 | 70 |
err = apiClient.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, headers) |
| 70 | 71 |
if err != nil {
|
| ... | ... |
@@ -107,6 +107,7 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, |
| 107 | 107 |
return err |
| 108 | 108 |
} |
| 109 | 109 |
|
| 110 |
+ // Get returns "" if the header does not exist |
|
| 110 | 111 |
encodedAuth := r.Header.Get("X-Registry-Auth")
|
| 111 | 112 |
|
| 112 | 113 |
id, err := sr.backend.CreateService(service, encodedAuth) |
| ... | ... |
@@ -132,6 +133,7 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, |
| 132 | 132 |
return fmt.Errorf("Invalid service version '%s': %s", rawVersion, err.Error())
|
| 133 | 133 |
} |
| 134 | 134 |
|
| 135 |
+ // Get returns "" if the header does not exist |
|
| 135 | 136 |
encodedAuth := r.Header.Get("X-Registry-Auth")
|
| 136 | 137 |
|
| 137 | 138 |
if err := sr.backend.UpdateService(vars["id"], version, service, encodedAuth); err != nil {
|
| ... | ... |
@@ -684,7 +684,11 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string) (string |
| 684 | 684 |
} |
| 685 | 685 |
|
| 686 | 686 |
if encodedAuth != "" {
|
| 687 |
- serviceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container).Container.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth}
|
|
| 687 |
+ ctnr := serviceSpec.Task.GetContainer() |
|
| 688 |
+ if ctnr == nil {
|
|
| 689 |
+ return "", fmt.Errorf("service does not use container tasks")
|
|
| 690 |
+ } |
|
| 691 |
+ ctnr.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth}
|
|
| 688 | 692 |
} |
| 689 | 693 |
|
| 690 | 694 |
r, err := c.client.CreateService(ctx, &swarmapi.CreateServiceRequest{Spec: &serviceSpec})
|
| ... | ... |
@@ -726,7 +730,23 @@ func (c *Cluster) UpdateService(serviceID string, version uint64, spec types.Ser |
| 726 | 726 |
} |
| 727 | 727 |
|
| 728 | 728 |
if encodedAuth != "" {
|
| 729 |
- serviceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container).Container.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth}
|
|
| 729 |
+ ctnr := serviceSpec.Task.GetContainer() |
|
| 730 |
+ if ctnr == nil {
|
|
| 731 |
+ return fmt.Errorf("service does not use container tasks")
|
|
| 732 |
+ } |
|
| 733 |
+ ctnr.PullOptions = &swarmapi.ContainerSpec_PullOptions{RegistryAuth: encodedAuth}
|
|
| 734 |
+ } else {
|
|
| 735 |
+ // this is needed because if the encodedAuth isn't being updated then we |
|
| 736 |
+ // shouldn't lose it, and continue to use the one that was already present |
|
| 737 |
+ currentService, err := getService(c.getRequestContext(), c.client, serviceID) |
|
| 738 |
+ if err != nil {
|
|
| 739 |
+ return err |
|
| 740 |
+ } |
|
| 741 |
+ ctnr := currentService.Spec.Task.GetContainer() |
|
| 742 |
+ if ctnr == nil {
|
|
| 743 |
+ return fmt.Errorf("service does not use container tasks")
|
|
| 744 |
+ } |
|
| 745 |
+ serviceSpec.Task.GetContainer().PullOptions = ctnr.PullOptions |
|
| 730 | 746 |
} |
| 731 | 747 |
|
| 732 | 748 |
_, err = c.client.UpdateService( |