mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #28076 from yongtang/25644-docker-service-tty
Add `--tty` to `docker service create/update`
This commit is contained in:
commit
69efb4652c
9 changed files with 96 additions and 2 deletions
|
@ -294,6 +294,7 @@ type serviceOptions struct {
|
||||||
workdir string
|
workdir string
|
||||||
user string
|
user string
|
||||||
groups []string
|
groups []string
|
||||||
|
tty bool
|
||||||
mounts opts.MountOpt
|
mounts opts.MountOpt
|
||||||
|
|
||||||
resources resourceOptions
|
resources resourceOptions
|
||||||
|
@ -365,6 +366,7 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
|
||||||
Dir: opts.workdir,
|
Dir: opts.workdir,
|
||||||
User: opts.user,
|
User: opts.user,
|
||||||
Groups: opts.groups,
|
Groups: opts.groups,
|
||||||
|
TTY: opts.tty,
|
||||||
Mounts: opts.mounts.Value(),
|
Mounts: opts.mounts.Value(),
|
||||||
StopGracePeriod: opts.stopGrace.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.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.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.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK")
|
||||||
|
|
||||||
|
flags.BoolVarP(&opts.tty, flagTTY, "t", false, "Allocate a pseudo-TTY")
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -490,6 +494,7 @@ const (
|
||||||
flagRestartMaxAttempts = "restart-max-attempts"
|
flagRestartMaxAttempts = "restart-max-attempts"
|
||||||
flagRestartWindow = "restart-window"
|
flagRestartWindow = "restart-window"
|
||||||
flagStopGracePeriod = "stop-grace-period"
|
flagStopGracePeriod = "stop-grace-period"
|
||||||
|
flagTTY = "tty"
|
||||||
flagUpdateDelay = "update-delay"
|
flagUpdateDelay = "update-delay"
|
||||||
flagUpdateFailureAction = "update-failure-action"
|
flagUpdateFailureAction = "update-failure-action"
|
||||||
flagUpdateMaxFailureRatio = "update-max-failure-ratio"
|
flagUpdateMaxFailureRatio = "update-max-failure-ratio"
|
||||||
|
|
|
@ -274,6 +274,14 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if flags.Changed(flagTTY) {
|
||||||
|
tty, err := flags.GetBool(flagTTY)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cspec.TTY = tty
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
|
||||||
Dir: c.Dir,
|
Dir: c.Dir,
|
||||||
User: c.User,
|
User: c.User,
|
||||||
Groups: c.Groups,
|
Groups: c.Groups,
|
||||||
|
TTY: c.TTY,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mounts
|
// Mounts
|
||||||
|
@ -77,6 +78,7 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
||||||
Dir: c.Dir,
|
Dir: c.Dir,
|
||||||
User: c.User,
|
User: c.User,
|
||||||
Groups: c.Groups,
|
Groups: c.Groups,
|
||||||
|
TTY: c.TTY,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.StopGracePeriod != nil {
|
if c.StopGracePeriod != nil {
|
||||||
|
|
|
@ -127,6 +127,7 @@ func (c *containerConfig) image() string {
|
||||||
func (c *containerConfig) config() *enginecontainer.Config {
|
func (c *containerConfig) config() *enginecontainer.Config {
|
||||||
config := &enginecontainer.Config{
|
config := &enginecontainer.Config{
|
||||||
Labels: c.labels(),
|
Labels: c.labels(),
|
||||||
|
Tty: c.spec().TTY,
|
||||||
User: c.spec().User,
|
User: c.spec().User,
|
||||||
Hostname: c.spec().Hostname,
|
Hostname: c.spec().Hostname,
|
||||||
Env: c.spec().Env,
|
Env: c.spec().Env,
|
||||||
|
|
|
@ -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.
|
* 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.
|
* `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.
|
* 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
|
### v1.24 API changes
|
||||||
|
|
||||||
|
|
|
@ -5112,7 +5112,8 @@ image](#create-an-image) section for more details.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"User": "33"
|
"User": "33",
|
||||||
|
"TTY": false
|
||||||
},
|
},
|
||||||
"LogDriver": {
|
"LogDriver": {
|
||||||
"Name": "json-file",
|
"Name": "json-file",
|
||||||
|
@ -5189,6 +5190,7 @@ image](#create-an-image) section for more details.
|
||||||
- **User** – A string value specifying the user inside the container.
|
- **User** – A string value specifying the user inside the container.
|
||||||
- **Labels** – A map of labels to associate with the service (e.g.,
|
- **Labels** – A map of labels to associate with the service (e.g.,
|
||||||
`{"key":"value", "key2":"value2"}`).
|
`{"key":"value", "key2":"value2"}`).
|
||||||
|
- **TTY** – A boolean indicating whether a pseudo-TTY should be allocated.
|
||||||
- **Mounts** – Specification for mounts to be added to containers
|
- **Mounts** – Specification for mounts to be added to containers
|
||||||
created as part of the service.
|
created as part of the service.
|
||||||
- **Target** – Container path.
|
- **Target** – Container path.
|
||||||
|
@ -5390,7 +5392,8 @@ image](#create-an-image) section for more details.
|
||||||
"Image": "busybox",
|
"Image": "busybox",
|
||||||
"Args": [
|
"Args": [
|
||||||
"top"
|
"top"
|
||||||
]
|
],
|
||||||
|
"TTY": true
|
||||||
},
|
},
|
||||||
"Resources": {
|
"Resources": {
|
||||||
"Limits": {},
|
"Limits": {},
|
||||||
|
@ -5438,6 +5441,7 @@ image](#create-an-image) section for more details.
|
||||||
- **User** – A string value specifying the user inside the container.
|
- **User** – A string value specifying the user inside the container.
|
||||||
- **Labels** – A map of labels to associate with the service (e.g.,
|
- **Labels** – A map of labels to associate with the service (e.g.,
|
||||||
`{"key":"value", "key2":"value2"}`).
|
`{"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
|
- **Mounts** – Specification for mounts to be added to containers created as part of the new
|
||||||
service.
|
service.
|
||||||
- **Target** – Container path.
|
- **Target** – Container path.
|
||||||
|
|
|
@ -52,6 +52,7 @@ Options:
|
||||||
--restart-max-attempts value Maximum number of restarts before giving up (default none)
|
--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)
|
--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)
|
--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-delay duration Delay between updates
|
||||||
--update-failure-action string Action on update failure (pause|continue) (default "pause")
|
--update-failure-action string Action on update failure (pause|continue) (default "pause")
|
||||||
--update-max-failure-ratio value Failure rate to tolerate during an update
|
--update-max-failure-ratio value Failure rate to tolerate during an update
|
||||||
|
|
|
@ -58,6 +58,7 @@ Options:
|
||||||
--restart-window value Window used to evaluate the restart policy (default none)
|
--restart-window value Window used to evaluate the restart policy (default none)
|
||||||
--rollback Rollback to previous specification
|
--rollback Rollback to previous specification
|
||||||
--stop-grace-period value Time to wait before force killing a container (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-delay duration Delay between updates
|
||||||
--update-failure-action string Action on update failure (pause|continue) (default "pause")
|
--update-failure-action string Action on update failure (pause|continue) (default "pause")
|
||||||
--update-max-failure-ratio value Failure rate to tolerate during an update
|
--update-max-failure-ratio value Failure rate to tolerate during an update
|
||||||
|
|
|
@ -718,3 +718,74 @@ func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(out, checker.Contains, "[VAR1=C VAR2]")
|
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")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue