Browse Source

Support `--group-add` and `--group-rm` in `docker service create/update`

This fix tries to address the issue raised in 25304 to support
`--group-add` and `--group-rm` in `docker service create`.

This fix adds `--group-add` to `docker service create` and `docker service update`,
adds `--group-rm` to `docker service update`.

This fix updates docs for `docker service create` and `docker service update`:
1. Add `--group-add` to `docker service create` and `docker service update`
2. Add `--group-rm` to `docker service update`

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Yong Tang 9 năm trước cách đây
mục cha
commit
b31969ee36

+ 5 - 0
api/client/service/opts.go

@@ -399,6 +399,7 @@ type serviceOptions struct {
 	env             opts.ListOpts
 	workdir         string
 	user            string
+	groups          []string
 	mounts          MountOpt
 
 	resources resourceOptions
@@ -446,6 +447,7 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
 				Labels:          runconfigopts.ConvertKVStringsToMap(opts.containerLabels.GetAll()),
 				Dir:             opts.workdir,
 				User:            opts.user,
+				Groups:          opts.groups,
 				Mounts:          opts.mounts.Value(),
 				StopGracePeriod: opts.stopGrace.Value(),
 			},
@@ -491,6 +493,7 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
 
 	flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container")
 	flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
+	flags.StringSliceVar(&opts.groups, flagGroupAdd, []string{}, "Add additional user groups to the container")
 
 	flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
 	flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
@@ -528,6 +531,8 @@ const (
 	flagEnv                  = "env"
 	flagEnvRemove            = "env-rm"
 	flagEnvAdd               = "env-add"
+	flagGroupAdd             = "group-add"
+	flagGroupRemove          = "group-rm"
 	flagLabel                = "label"
 	flagLabelRemove          = "label-rm"
 	flagLabelAdd             = "label-add"

+ 30 - 0
api/client/service/update.go

@@ -39,6 +39,7 @@ func newUpdateCommand(dockerCli *client.DockerCli) *cobra.Command {
 	addServiceFlags(cmd, opts)
 
 	flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
+	flags.Var(newListOptsVar(), flagGroupRemove, "Remove previously added user groups from the container")
 	flags.Var(newListOptsVar(), flagLabelRemove, "Remove a label by its key")
 	flags.Var(newListOptsVar(), flagContainerLabelRemove, "Remove a container label by its key")
 	flags.Var(newListOptsVar(), flagMountRemove, "Remove a mount by its target path")
@@ -211,6 +212,12 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
 		spec.EndpointSpec.Mode = swarm.ResolutionMode(value)
 	}
 
+	if anyChanged(flags, flagGroupAdd, flagGroupRemove) {
+		if err := updateGroups(flags, &cspec.Groups); err != nil {
+			return err
+		}
+	}
+
 	if anyChanged(flags, flagPublishAdd, flagPublishRemove) {
 		if spec.EndpointSpec == nil {
 			spec.EndpointSpec = &swarm.EndpointSpec{}
@@ -370,6 +377,29 @@ func updateMounts(flags *pflag.FlagSet, mounts *[]mounttypes.Mount) {
 	*mounts = newMounts
 }
 
+func updateGroups(flags *pflag.FlagSet, groups *[]string) error {
+	if flags.Changed(flagGroupAdd) {
+		values, err := flags.GetStringSlice(flagGroupAdd)
+		if err != nil {
+			return err
+		}
+		*groups = append(*groups, values...)
+	}
+	toRemove := buildToRemoveSet(flags, flagGroupRemove)
+
+	newGroups := []string{}
+	for _, group := range *groups {
+		if _, exists := toRemove[group]; !exists {
+			newGroups = append(newGroups, group)
+		}
+	}
+	// Sort so that result is predictable.
+	sort.Strings(newGroups)
+
+	*groups = newGroups
+	return nil
+}
+
 type byPortConfig []swarm.PortConfig
 
 func (r byPortConfig) Len() int      { return len(r) }

+ 17 - 0
api/client/service/update_test.go

@@ -100,6 +100,23 @@ func TestUpdateEnvironmentWithDuplicateKeys(t *testing.T) {
 	assert.Equal(t, envs[0], "A=b")
 }
 
+func TestUpdateGroups(t *testing.T) {
+	flags := newUpdateCommand(nil).Flags()
+	flags.Set("group-add", "wheel")
+	flags.Set("group-add", "docker")
+	flags.Set("group-rm", "root")
+	flags.Set("group-add", "foo")
+	flags.Set("group-rm", "docker")
+
+	groups := []string{"bar", "root"}
+
+	updateGroups(flags, &groups)
+	assert.Equal(t, len(groups), 3)
+	assert.Equal(t, groups[0], "bar")
+	assert.Equal(t, groups[1], "foo")
+	assert.Equal(t, groups[2], "wheel")
+}
+
 func TestUpdateMounts(t *testing.T) {
 	flags := newUpdateCommand(nil).Flags()
 	flags.Set("mount-add", "type=volume,target=/toadd")

+ 2 - 0
daemon/cluster/convert/container.go

@@ -19,6 +19,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
 		Env:     c.Env,
 		Dir:     c.Dir,
 		User:    c.User,
+		Groups:  c.Groups,
 	}
 
 	// Mounts
@@ -67,6 +68,7 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
 		Env:     c.Env,
 		Dir:     c.Dir,
 		User:    c.User,
+		Groups:  c.Groups,
 	}
 
 	if c.StopGracePeriod != nil {

+ 1 - 0
docs/reference/commandline/service_create.md

@@ -20,6 +20,7 @@ Options:
       --container-label value          Service container labels (default [])
       --endpoint-mode string           Endpoint mode (vip or dnsrr)
   -e, --env value                      Set environment variables (default [])
+      --group-add value                Add additional user groups to the container (default [])
       --help                           Print usage
   -l, --label value                    Service labels (default [])
       --limit-cpu value                Limit CPUs (default 0.000)

+ 2 - 0
docs/reference/commandline/service_update.md

@@ -24,6 +24,8 @@ Options:
       --endpoint-mode string           Endpoint mode (vip or dnsrr)
       --env-add value                  Add or update environment variables (default [])
       --env-rm value                   Remove an environment variable (default [])
+      --group-add value                Add additional user groups to the container (default [])
+      --group-rm value                 Remove previously added user groups from the container (default [])
       --help                           Print usage
       --image string                   Service image tag
       --label-add value                Add or update service labels (default [])

+ 22 - 0
integration-cli/docker_cli_swarm_test.go

@@ -220,3 +220,25 @@ func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) {
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "[{ tcp 20 80}]")
 }
+
+func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *check.C) {
+	d := s.AddDaemon(c, true, true)
+
+	name := "top"
+	out, err := d.Cmd("service", "create", "--name", name, "--user", "root:root", "--group-add", "wheel", "--group-add", "audio", "--group-add", "staff", "--group-add", "777", "busybox", "sh", "-c", "id > /id && top")
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+
+	// make sure task has been deployed.
+	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
+
+	out, err = d.Cmd("ps", "-q")
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+
+	container := strings.TrimSpace(out)
+
+	out, err = d.Cmd("exec", container, "cat", "/id")
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Equals, "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777")
+}