mirror of https://github.com/docker/cli.git
service: Add --cap-add & --cap-drop to service cmds
Signed-off-by: Albin Kerouanton <albin@akerouanton.name>
This commit is contained in:
parent
0db61ff6da
commit
c6ec4e081e
|
@ -97,6 +97,15 @@ ContainerSpec:
|
||||||
{{- if .ContainerUser }}
|
{{- if .ContainerUser }}
|
||||||
User: {{ .ContainerUser }}
|
User: {{ .ContainerUser }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .HasCapabilities }}
|
||||||
|
Capabilities:
|
||||||
|
{{- if .HasCapabilityAdd }}
|
||||||
|
Add: {{ .CapabilityAdd }}
|
||||||
|
{{- end }}
|
||||||
|
{{- if .HasCapabilityDrop }}
|
||||||
|
Drop: {{ .CapabilityDrop }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
{{- if .ContainerSysCtls }}
|
{{- if .ContainerSysCtls }}
|
||||||
SysCtls:
|
SysCtls:
|
||||||
{{- range $k, $v := .ContainerSysCtls }}
|
{{- range $k, $v := .ContainerSysCtls }}
|
||||||
|
@ -532,6 +541,26 @@ func (ctx *serviceInspectContext) Ports() []swarm.PortConfig {
|
||||||
return ctx.Service.Endpoint.Ports
|
return ctx.Service.Endpoint.Ports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasCapabilities() bool {
|
||||||
|
return len(ctx.Service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd) > 0 || len(ctx.Service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasCapabilityAdd() bool {
|
||||||
|
return len(ctx.Service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) HasCapabilityDrop() bool {
|
||||||
|
return len(ctx.Service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) CapabilityAdd() string {
|
||||||
|
return strings.Join(ctx.Service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *serviceInspectContext) CapabilityDrop() string {
|
||||||
|
return strings.Join(ctx.Service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}\t{{.Ports}}"
|
defaultServiceTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Mode}}\t{{.Replicas}}\t{{.Image}}\t{{.Ports}}"
|
||||||
|
|
||||||
|
|
|
@ -506,6 +506,8 @@ type serviceOptions struct {
|
||||||
dnsOption opts.ListOpts
|
dnsOption opts.ListOpts
|
||||||
hosts opts.ListOpts
|
hosts opts.ListOpts
|
||||||
sysctls opts.ListOpts
|
sysctls opts.ListOpts
|
||||||
|
capAdd opts.ListOpts
|
||||||
|
capDrop opts.ListOpts
|
||||||
|
|
||||||
resources resourceOptions
|
resources resourceOptions
|
||||||
stopGrace opts.DurationOpt
|
stopGrace opts.DurationOpt
|
||||||
|
@ -549,6 +551,8 @@ func newServiceOptions() *serviceOptions {
|
||||||
dnsSearch: opts.NewListOpts(opts.ValidateDNSSearch),
|
dnsSearch: opts.NewListOpts(opts.ValidateDNSSearch),
|
||||||
hosts: opts.NewListOpts(opts.ValidateExtraHost),
|
hosts: opts.NewListOpts(opts.ValidateExtraHost),
|
||||||
sysctls: opts.NewListOpts(nil),
|
sysctls: opts.NewListOpts(nil),
|
||||||
|
capAdd: opts.NewListOpts(nil),
|
||||||
|
capDrop: opts.NewListOpts(nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,6 +720,8 @@ func (options *serviceOptions) ToService(ctx context.Context, apiClient client.N
|
||||||
Healthcheck: healthConfig,
|
Healthcheck: healthConfig,
|
||||||
Isolation: container.Isolation(options.isolation),
|
Isolation: container.Isolation(options.isolation),
|
||||||
Sysctls: opts.ConvertKVStringsToMap(options.sysctls.GetAll()),
|
Sysctls: opts.ConvertKVStringsToMap(options.sysctls.GetAll()),
|
||||||
|
CapabilityAdd: options.capAdd.GetAll(),
|
||||||
|
CapabilityDrop: options.capDrop.GetAll(),
|
||||||
},
|
},
|
||||||
Networks: networks,
|
Networks: networks,
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
|
@ -818,6 +824,10 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu
|
||||||
flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
|
flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
|
||||||
flags.SetAnnotation(flagHostname, "version", []string{"1.25"})
|
flags.SetAnnotation(flagHostname, "version", []string{"1.25"})
|
||||||
flags.Var(&opts.entrypoint, flagEntrypoint, "Overwrite the default ENTRYPOINT of the image")
|
flags.Var(&opts.entrypoint, flagEntrypoint, "Overwrite the default ENTRYPOINT of the image")
|
||||||
|
flags.Var(&opts.capAdd, flagCapAdd, "Add Linux capabilities")
|
||||||
|
flags.SetAnnotation(flagCapAdd, "version", []string{"1.41"})
|
||||||
|
flags.Var(&opts.capDrop, flagCapDrop, "Drop Linux capabilities")
|
||||||
|
flags.SetAnnotation(flagCapDrop, "version", []string{"1.41"})
|
||||||
|
|
||||||
flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
|
flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
|
||||||
flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
|
flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")
|
||||||
|
@ -1001,6 +1011,8 @@ const (
|
||||||
flagConfigAdd = "config-add"
|
flagConfigAdd = "config-add"
|
||||||
flagConfigRemove = "config-rm"
|
flagConfigRemove = "config-rm"
|
||||||
flagIsolation = "isolation"
|
flagIsolation = "isolation"
|
||||||
|
flagCapAdd = "cap-add"
|
||||||
|
flagCapDrop = "cap-drop"
|
||||||
)
|
)
|
||||||
|
|
||||||
func validateAPIVersion(c swarm.ServiceSpec, serverAPIVersion string) error {
|
func validateAPIVersion(c swarm.ServiceSpec, serverAPIVersion string) error {
|
||||||
|
|
|
@ -505,6 +505,7 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags
|
||||||
}
|
}
|
||||||
|
|
||||||
updateString(flagStopSignal, &cspec.StopSignal)
|
updateString(flagStopSignal, &cspec.StopSignal)
|
||||||
|
updateCapabilities(flags, cspec)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1349,3 +1350,41 @@ func updateCredSpecConfig(flags *pflag.FlagSet, containerSpec *swarm.ContainerSp
|
||||||
containerSpec.Privileges.CredentialSpec = credSpec
|
containerSpec.Privileges.CredentialSpec = credSpec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateCapabilities(flags *pflag.FlagSet, containerSpec *swarm.ContainerSpec) {
|
||||||
|
var addToRemove, dropToRemove map[string]struct{}
|
||||||
|
capAdd := containerSpec.CapabilityAdd
|
||||||
|
capDrop := containerSpec.CapabilityDrop
|
||||||
|
|
||||||
|
// First add the capabilities passed to --cap-add to the list of requested caps
|
||||||
|
if flags.Changed(flagCapAdd) {
|
||||||
|
caps := flags.Lookup(flagCapAdd).Value.(*opts.ListOpts).GetAll()
|
||||||
|
capAdd = append(capAdd, caps...)
|
||||||
|
|
||||||
|
dropToRemove = buildToRemoveSet(flags, flagCapAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And add the capabilities passed to --cap-drop to the list of dropped caps
|
||||||
|
if flags.Changed(flagCapDrop) {
|
||||||
|
caps := flags.Lookup(flagCapDrop).Value.(*opts.ListOpts).GetAll()
|
||||||
|
capDrop = append(capDrop, caps...)
|
||||||
|
|
||||||
|
addToRemove = buildToRemoveSet(flags, flagCapDrop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then take care of removing caps passed to --cap-drop from the list of requested caps
|
||||||
|
containerSpec.CapabilityAdd = make([]string, 0, len(capAdd))
|
||||||
|
for _, cap := range capAdd {
|
||||||
|
if _, exists := addToRemove[cap]; !exists {
|
||||||
|
containerSpec.CapabilityAdd = append(containerSpec.CapabilityAdd, cap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And remove the caps passed to --cap-add from the list of caps to drop
|
||||||
|
containerSpec.CapabilityDrop = make([]string, 0, len(capDrop))
|
||||||
|
for _, cap := range capDrop {
|
||||||
|
if _, exists := dropToRemove[cap]; !exists {
|
||||||
|
containerSpec.CapabilityDrop = append(containerSpec.CapabilityDrop, cap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1342,3 +1342,74 @@ func TestUpdateCredSpec(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateCaps(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
// name is the name of the testcase
|
||||||
|
name string
|
||||||
|
// flagAdd is the value passed to --cap-add
|
||||||
|
flagAdd []string
|
||||||
|
// flagDrop is the value passed to --cap-drop
|
||||||
|
flagDrop []string
|
||||||
|
// spec is the original ContainerSpec, before being updated
|
||||||
|
spec *swarm.ContainerSpec
|
||||||
|
// expectedAdd is the set of requested caps the ContainerSpec should have once updated
|
||||||
|
expectedAdd []string
|
||||||
|
// expectedDrop is the set of dropped caps the ContainerSpec should have once updated
|
||||||
|
expectedDrop []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Add new caps",
|
||||||
|
flagAdd: []string{"NET_ADMIN"},
|
||||||
|
flagDrop: []string{},
|
||||||
|
spec: &swarm.ContainerSpec{},
|
||||||
|
expectedAdd: []string{"NET_ADMIN"},
|
||||||
|
expectedDrop: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Drop new caps",
|
||||||
|
flagAdd: []string{},
|
||||||
|
flagDrop: []string{"CAP_MKNOD"},
|
||||||
|
spec: &swarm.ContainerSpec{},
|
||||||
|
expectedAdd: []string{},
|
||||||
|
expectedDrop: []string{"CAP_MKNOD"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Add a previously dropped cap",
|
||||||
|
flagAdd: []string{"NET_ADMIN"},
|
||||||
|
flagDrop: []string{},
|
||||||
|
spec: &swarm.ContainerSpec{
|
||||||
|
CapabilityDrop: []string{"NET_ADMIN"},
|
||||||
|
},
|
||||||
|
expectedAdd: []string{"NET_ADMIN"},
|
||||||
|
expectedDrop: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Drop a previously requested cap",
|
||||||
|
flagAdd: []string{},
|
||||||
|
flagDrop: []string{"CAP_MKNOD"},
|
||||||
|
spec: &swarm.ContainerSpec{
|
||||||
|
CapabilityAdd: []string{"CAP_MKNOD"},
|
||||||
|
},
|
||||||
|
expectedAdd: []string{},
|
||||||
|
expectedDrop: []string{"CAP_MKNOD"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
flags := newUpdateCommand(nil).Flags()
|
||||||
|
for _, cap := range tc.flagAdd {
|
||||||
|
flags.Set(flagCapAdd, cap)
|
||||||
|
}
|
||||||
|
for _, cap := range tc.flagDrop {
|
||||||
|
flags.Set(flagCapDrop, cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCapabilities(flags, tc.spec)
|
||||||
|
|
||||||
|
assert.DeepEqual(t, tc.spec.CapabilityAdd, tc.expectedAdd)
|
||||||
|
assert.DeepEqual(t, tc.spec.CapabilityDrop, tc.expectedDrop)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue