Add --tty to docker service create/update

This fix tries to add `--tty` to `docker service create/update`. As was
specified in 25644, `TTY` flag has been added to SwarmKit and is
already vendored.

This fix add `--tty` to `docker service create/update`.

Related document has been updated.

Additional integration tests has been added.

This fix fixes 25644.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
Yong Tang 2016-11-04 11:31:44 -07:00
parent 05b249e70f
commit 599be5a551
9 changed files with 96 additions and 2 deletions

View file

@ -294,6 +294,7 @@ type serviceOptions struct {
workdir string
user string
groups []string
tty bool
mounts opts.MountOpt
resources resourceOptions
@ -365,6 +366,7 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
Dir: opts.workdir,
User: opts.user,
Groups: opts.groups,
TTY: opts.tty,
Mounts: opts.mounts.Value(),
StopGracePeriod: opts.stopGrace.Value(),
},
@ -450,6 +452,8 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run")
flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy")
flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK")
flags.BoolVarP(&opts.tty, flagTTY, "t", false, "Allocate a pseudo-TTY")
}
const (
@ -490,6 +494,7 @@ const (
flagRestartMaxAttempts = "restart-max-attempts"
flagRestartWindow = "restart-window"
flagStopGracePeriod = "stop-grace-period"
flagTTY = "tty"
flagUpdateDelay = "update-delay"
flagUpdateFailureAction = "update-failure-action"
flagUpdateMaxFailureRatio = "update-max-failure-ratio"

View file

@ -274,6 +274,14 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
return err
}
if flags.Changed(flagTTY) {
tty, err := flags.GetBool(flagTTY)
if err != nil {
return err
}
cspec.TTY = tty
}
return nil
}

View file

@ -22,6 +22,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
Dir: c.Dir,
User: c.User,
Groups: c.Groups,
TTY: c.TTY,
}
// Mounts
@ -77,6 +78,7 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
Dir: c.Dir,
User: c.User,
Groups: c.Groups,
TTY: c.TTY,
}
if c.StopGracePeriod != nil {

View file

@ -127,6 +127,7 @@ func (c *containerConfig) image() string {
func (c *containerConfig) config() *enginecontainer.Config {
config := &enginecontainer.Config{
Labels: c.labels(),
Tty: c.spec().TTY,
User: c.spec().User,
Hostname: c.spec().Hostname,
Env: c.spec().Env,

View file

@ -167,6 +167,7 @@ This section lists each version from latest to oldest. Each listing includes a
* The `HostConfig` field now includes `NanoCPUs` that represents CPU quota in units of 10<sup>-9</sup> CPUs.
* `GET /info` now returns more structured information about security options.
* The `HostConfig` field now includes `CpuCount` that represents the number of CPUs available for execution by the container. Windows daemon only.
* `POST /services/create` and `POST /services/(id or name)/update` now accept the `TTY` parameter, which allocate a pseudo-TTY in container.
### v1.24 API changes

View file

@ -5102,7 +5102,8 @@ image](#create-an-image) section for more details.
}
}
],
"User": "33"
"User": "33",
"TTY": false
},
"LogDriver": {
"Name": "json-file",
@ -5179,6 +5180,7 @@ image](#create-an-image) section for more details.
- **User** A string value specifying the user inside the container.
- **Labels** A map of labels to associate with the service (e.g.,
`{"key":"value", "key2":"value2"}`).
- **TTY** A boolean indicating whether a pseudo-TTY should be allocated.
- **Mounts** Specification for mounts to be added to containers
created as part of the service.
- **Target** Container path.
@ -5380,7 +5382,8 @@ image](#create-an-image) section for more details.
"Image": "busybox",
"Args": [
"top"
]
],
"TTY": true
},
"Resources": {
"Limits": {},
@ -5428,6 +5431,7 @@ image](#create-an-image) section for more details.
- **User** A string value specifying the user inside the container.
- **Labels** A map of labels to associate with the service (e.g.,
`{"key":"value", "key2":"value2"}`).
- **TTY** A boolean indicating whether a pseudo-TTY should be allocated.
- **Mounts** Specification for mounts to be added to containers created as part of the new
service.
- **Target** Container path.

View file

@ -52,6 +52,7 @@ Options:
--restart-max-attempts value Maximum number of restarts before giving up (default none)
--restart-window value Window used to evaluate the restart policy (default none)
--stop-grace-period value Time to wait before force killing a container (default none)
-t, --tty Allocate a pseudo-TTY
--update-delay duration Delay between updates
--update-failure-action string Action on update failure (pause|continue) (default "pause")
--update-max-failure-ratio value Failure rate to tolerate during an update

View file

@ -58,6 +58,7 @@ Options:
--restart-window value Window used to evaluate the restart policy (default none)
--rollback Rollback to previous specification
--stop-grace-period value Time to wait before force killing a container (default none)
-t, --tty Allocate a pseudo-TTY
--update-delay duration Delay between updates
--update-failure-action string Action on update failure (pause|continue) (default "pause")
--update-max-failure-ratio value Failure rate to tolerate during an update

View file

@ -718,3 +718,74 @@ func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *check.C) {
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "[VAR1=C VAR2]")
}
func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) {
d := s.AddDaemon(c, true, true)
name := "top"
ttyCheck := "if [ -t 0 ]; then echo TTY > /status && top; else echo none > /status && top; fi"
// Without --tty
expectedOutput := "none"
out, err := d.Cmd("service", "create", "--name", name, "busybox", "sh", "-c", ttyCheck)
c.Assert(err, checker.IsNil)
// Make sure task has been deployed.
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
// We need to get the container id.
out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
c.Assert(err, checker.IsNil)
id := strings.TrimSpace(out)
out, err = d.Cmd("exec", id, "cat", "/status")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
// Remove service
out, err = d.Cmd("service", "rm", name)
c.Assert(err, checker.IsNil)
// Make sure container has been destroyed.
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0)
// With --tty
expectedOutput = "TTY"
out, err = d.Cmd("service", "create", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck)
c.Assert(err, checker.IsNil)
// Make sure task has been deployed.
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
// We need to get the container id.
out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
c.Assert(err, checker.IsNil)
id = strings.TrimSpace(out)
out, err = d.Cmd("exec", id, "cat", "/status")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
}
func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *check.C) {
d := s.AddDaemon(c, true, true)
// Create a service
name := "top"
_, err := d.Cmd("service", "create", "--name", name, "busybox", "top")
c.Assert(err, checker.IsNil)
// Make sure task has been deployed.
waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Equals, "false")
_, err = d.Cmd("service", "update", "--tty", name)
c.Assert(err, checker.IsNil)
out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Equals, "true")
}