Merge 13edc7f4f1
into b7c059886c
This commit is contained in:
commit
f6ee27771d
5 changed files with 123 additions and 12 deletions
|
@ -13,15 +13,20 @@ import (
|
|||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/builder/dockerfile"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type mergeOptions struct {
|
||||
keepReservedLabels bool
|
||||
}
|
||||
|
||||
// merge merges two Config, the image container configuration (defaults values),
|
||||
// and the user container configuration, either passed by the API or generated
|
||||
// by the cli.
|
||||
// It will mutate the specified user configuration (userConf) with the image
|
||||
// configuration where the user configuration is incomplete.
|
||||
func merge(userConf, imageConf *containertypes.Config) error {
|
||||
func merge(userConf, imageConf *containertypes.Config, options mergeOptions) error {
|
||||
if userConf.User == "" {
|
||||
userConf.User = imageConf.User
|
||||
}
|
||||
|
@ -63,6 +68,9 @@ func merge(userConf, imageConf *containertypes.Config) error {
|
|||
userConf.Labels = map[string]string{}
|
||||
}
|
||||
for l, v := range imageConf.Labels {
|
||||
if !options.keepReservedLabels && opts.IsReservedLabelNamespace(l) {
|
||||
continue
|
||||
}
|
||||
if _, ok := userConf.Labels[l]; !ok {
|
||||
userConf.Labels[l] = v
|
||||
}
|
||||
|
@ -155,7 +163,7 @@ func (daemon *Daemon) CreateImageFromContainer(ctx context.Context, name string,
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := merge(newConfig, container.Config); err != nil {
|
||||
if err := merge(newConfig, container.Config, mergeOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
|
|
@ -309,7 +309,7 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig)
|
|||
|
||||
func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error {
|
||||
if img != nil && img.Config != nil {
|
||||
if err := merge(config, img.Config); err != nil {
|
||||
if err := merge(config, img.Config, mergeOptions{keepReservedLabels: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,6 +232,7 @@ func TestMerge(t *testing.T) {
|
|||
ExposedPorts: portsImage,
|
||||
Env: []string{"VAR1=1", "VAR2=2"},
|
||||
Volumes: volumesImage,
|
||||
Labels: map[string]string{"com.docker.foo": "bar", "my-label": "my-value"},
|
||||
}
|
||||
|
||||
portsUser := make(nat.PortSet)
|
||||
|
@ -245,7 +246,7 @@ func TestMerge(t *testing.T) {
|
|||
Volumes: volumesUser,
|
||||
}
|
||||
|
||||
if err := merge(configUser, configImage); err != nil {
|
||||
if err := merge(configUser, configImage, mergeOptions{keepReservedLabels: true}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
|
@ -279,22 +280,30 @@ func TestMerge(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, configUser.Labels, configImage.Labels)
|
||||
|
||||
configImage2 := &containertypes.Config{
|
||||
ExposedPorts: ports,
|
||||
Labels: map[string]string{"com.docker.foo": "bar", "my-label": "my-value"},
|
||||
}
|
||||
configUser2 := &containertypes.Config{
|
||||
ExposedPorts: portsUser,
|
||||
}
|
||||
|
||||
if err := merge(configUser, configImage2); err != nil {
|
||||
if err := merge(configUser2, configImage2, mergeOptions{}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(configUser.ExposedPorts) != 4 {
|
||||
if len(configUser2.ExposedPorts) != 4 {
|
||||
t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
|
||||
}
|
||||
for portSpecs := range configUser.ExposedPorts {
|
||||
for portSpecs := range configUser2.ExposedPorts {
|
||||
if portSpecs.Port() != "0" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
|
||||
t.Fatalf("Expected %q or %q or %q or %q, found %s", 0, 1111, 2222, 3333, portSpecs)
|
||||
}
|
||||
}
|
||||
assert.DeepEqual(t, configUser2.Labels, map[string]string{"my-label": "my-value"})
|
||||
}
|
||||
|
||||
func TestValidateContainerIsolation(t *testing.T) {
|
||||
|
|
22
opts/opts.go
22
opts/opts.go
|
@ -337,13 +337,12 @@ func validateDomain(val string) (string, error) {
|
|||
// and returns it.
|
||||
// Labels are in the form on key=value.
|
||||
func ValidateLabel(val string) (string, error) {
|
||||
if strings.Count(val, "=") < 1 {
|
||||
kv := strings.SplitN(val, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
return "", fmt.Errorf("bad attribute format: %s", val)
|
||||
}
|
||||
|
||||
lowered := strings.ToLower(val)
|
||||
if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") ||
|
||||
strings.HasPrefix(lowered, "org.dockerproject.") {
|
||||
if IsReservedLabelNamespace(kv[0]) {
|
||||
return "", fmt.Errorf(
|
||||
"label %s is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use",
|
||||
val)
|
||||
|
@ -352,6 +351,21 @@ func ValidateLabel(val string) (string, error) {
|
|||
return val, nil
|
||||
}
|
||||
|
||||
var reservedLabelNamespaces = []string{"com.docker", "io.docker", "org.dockerproject"}
|
||||
|
||||
// IsReservedLabelNamespace checks if a given label uses a reserved namespace
|
||||
// Reserved namespaces are com.docker.*, io.docker.*, and org.dockerproject.*
|
||||
// (case insensitive).
|
||||
func IsReservedLabelNamespace(name string) bool {
|
||||
lowered := strings.ToLower(name)
|
||||
for _, ns := range reservedLabelNamespaces {
|
||||
if lowered == ns || strings.HasPrefix(lowered, ns+".") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateSingleGenericResource validates that a single entry in the
|
||||
// generic resource list is valid.
|
||||
// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
|
||||
|
|
|
@ -233,7 +233,7 @@ func TestValidateLabel(t *testing.T) {
|
|||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "lable with bad attribute format",
|
||||
name: "label with bad attribute format",
|
||||
label: "label",
|
||||
expectedErr: "bad attribute format: label",
|
||||
},
|
||||
|
@ -316,6 +316,86 @@ func TestValidateLabel(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIsReservedLabelNamespace(t *testing.T) {
|
||||
tests := []struct {
|
||||
label string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
label: "my-label",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
label: "com.dockerpsychnotreserved.label",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
label: "io.dockerproject.not",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
label: "org.docker.not",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
label: "com.docker",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "com.docker.",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "com.docker.feature",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "COM.docker.feature",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "io.docker",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "io.docker.",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "io.docker.feature",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "io.docker.feature",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "org.dockerproject",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "org.dockerproject.",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "org.dockerproject.feature",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
label: "org.dockerproject.feature",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
testCase := tc
|
||||
t.Run(testCase.label, func(t *testing.T) {
|
||||
result := IsReservedLabelNamespace(testCase.label)
|
||||
assert.Equal(t, result, testCase.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func logOptsValidator(val string) (string, error) {
|
||||
allowedKeys := map[string]string{"max-size": "1", "max-file": "2"}
|
||||
vals := strings.Split(val, "=")
|
||||
|
|
Loading…
Add table
Reference in a new issue