diff --git a/command/service/opts.go b/command/service/opts.go index 7e16957102..47d417fb25 100644 --- a/command/service/opts.go +++ b/command/service/opts.go @@ -238,6 +238,38 @@ func (r *restartPolicyOptions) ToRestartPolicy() *swarm.RestartPolicy { } } +type credentialSpecOpt struct { + value *swarm.CredentialSpec + source string +} + +func (c *credentialSpecOpt) Set(value string) error { + c.source = value + c.value = &swarm.CredentialSpec{} + switch { + case strings.HasPrefix(value, "file://"): + c.value.File = strings.TrimPrefix(value, "file://") + case strings.HasPrefix(value, "registry://"): + c.value.Registry = strings.TrimPrefix(value, "registry://") + default: + return errors.New("Invalid credential spec - value must be prefixed file:// or registry:// followed by a value") + } + + return nil +} + +func (c *credentialSpecOpt) Type() string { + return "credential-spec" +} + +func (c *credentialSpecOpt) String() string { + return c.source +} + +func (c *credentialSpecOpt) Value() *swarm.CredentialSpec { + return c.value +} + func convertNetworks(networks []string) []swarm.NetworkAttachmentConfig { nets := []swarm.NetworkAttachmentConfig{} for _, network := range networks { @@ -355,6 +387,7 @@ type serviceOptions struct { workdir string user string groups opts.ListOpts + credentialSpec credentialSpecOpt stopSignal string tty bool readOnly bool @@ -500,6 +533,12 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) { EndpointSpec: opts.endpoint.ToEndpointSpec(), } + if opts.credentialSpec.Value() != nil { + service.TaskTemplate.ContainerSpec.Privileges = &swarm.Privileges{ + CredentialSpec: opts.credentialSpec.Value(), + } + } + return service, nil } @@ -511,6 +550,8 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) { flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container") flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: [:])") + flags.Var(&opts.credentialSpec, flagCredentialSpec, "Credential spec for managed service account (Windows only)") + flags.SetAnnotation(flagCredentialSpec, "version", []string{"1.29"}) flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname") flags.SetAnnotation(flagHostname, "version", []string{"1.25"}) flags.Var(&opts.entrypoint, flagEntrypoint, "Overwrite the default ENTRYPOINT of the image") @@ -582,6 +623,7 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions) { } const ( + flagCredentialSpec = "credential-spec" flagPlacementPref = "placement-pref" flagPlacementPrefAdd = "placement-pref-add" flagPlacementPrefRemove = "placement-pref-rm"