diff --git a/components/engine/opts/mount.go b/components/engine/opts/mount.go deleted file mode 100644 index d4ccf838d9..0000000000 --- a/components/engine/opts/mount.go +++ /dev/null @@ -1,173 +0,0 @@ -package opts - -import ( - "encoding/csv" - "fmt" - "os" - "strconv" - "strings" - - mounttypes "github.com/docker/docker/api/types/mount" - "github.com/docker/go-units" -) - -// MountOpt is a Value type for parsing mounts -type MountOpt struct { - values []mounttypes.Mount -} - -// Set a new mount value -func (m *MountOpt) Set(value string) error { - csvReader := csv.NewReader(strings.NewReader(value)) - fields, err := csvReader.Read() - if err != nil { - return err - } - - mount := mounttypes.Mount{} - - volumeOptions := func() *mounttypes.VolumeOptions { - if mount.VolumeOptions == nil { - mount.VolumeOptions = &mounttypes.VolumeOptions{ - Labels: make(map[string]string), - } - } - if mount.VolumeOptions.DriverConfig == nil { - mount.VolumeOptions.DriverConfig = &mounttypes.Driver{} - } - return mount.VolumeOptions - } - - bindOptions := func() *mounttypes.BindOptions { - if mount.BindOptions == nil { - mount.BindOptions = new(mounttypes.BindOptions) - } - return mount.BindOptions - } - - tmpfsOptions := func() *mounttypes.TmpfsOptions { - if mount.TmpfsOptions == nil { - mount.TmpfsOptions = new(mounttypes.TmpfsOptions) - } - return mount.TmpfsOptions - } - - setValueOnMap := func(target map[string]string, value string) { - parts := strings.SplitN(value, "=", 2) - if len(parts) == 1 { - target[value] = "" - } else { - target[parts[0]] = parts[1] - } - } - - mount.Type = mounttypes.TypeVolume // default to volume mounts - // Set writable as the default - for _, field := range fields { - parts := strings.SplitN(field, "=", 2) - key := strings.ToLower(parts[0]) - - if len(parts) == 1 { - switch key { - case "readonly", "ro": - mount.ReadOnly = true - continue - case "volume-nocopy": - volumeOptions().NoCopy = true - continue - } - } - - if len(parts) != 2 { - return fmt.Errorf("invalid field '%s' must be a key=value pair", field) - } - - value := parts[1] - switch key { - case "type": - mount.Type = mounttypes.Type(strings.ToLower(value)) - case "source", "src": - mount.Source = value - case "target", "dst", "destination": - mount.Target = value - case "readonly", "ro": - mount.ReadOnly, err = strconv.ParseBool(value) - if err != nil { - return fmt.Errorf("invalid value for %s: %s", key, value) - } - case "consistency": - mount.Consistency = mounttypes.Consistency(strings.ToLower(value)) - case "bind-propagation": - bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value)) - case "volume-nocopy": - volumeOptions().NoCopy, err = strconv.ParseBool(value) - if err != nil { - return fmt.Errorf("invalid value for volume-nocopy: %s", value) - } - case "volume-label": - setValueOnMap(volumeOptions().Labels, value) - case "volume-driver": - volumeOptions().DriverConfig.Name = value - case "volume-opt": - if volumeOptions().DriverConfig.Options == nil { - volumeOptions().DriverConfig.Options = make(map[string]string) - } - setValueOnMap(volumeOptions().DriverConfig.Options, value) - case "tmpfs-size": - sizeBytes, err := units.RAMInBytes(value) - if err != nil { - return fmt.Errorf("invalid value for %s: %s", key, value) - } - tmpfsOptions().SizeBytes = sizeBytes - case "tmpfs-mode": - ui64, err := strconv.ParseUint(value, 8, 32) - if err != nil { - return fmt.Errorf("invalid value for %s: %s", key, value) - } - tmpfsOptions().Mode = os.FileMode(ui64) - default: - return fmt.Errorf("unexpected key '%s' in '%s'", key, field) - } - } - - if mount.Type == "" { - return fmt.Errorf("type is required") - } - - if mount.Target == "" { - return fmt.Errorf("target is required") - } - - if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume { - return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type) - } - if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind { - return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type) - } - if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs { - return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type) - } - - m.values = append(m.values, mount) - return nil -} - -// Type returns the type of this option -func (m *MountOpt) Type() string { - return "mount" -} - -// String returns a string repr of this option -func (m *MountOpt) String() string { - mounts := []string{} - for _, mount := range m.values { - repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target) - mounts = append(mounts, repr) - } - return strings.Join(mounts, ", ") -} - -// Value returns the mounts -func (m *MountOpt) Value() []mounttypes.Mount { - return m.values -} diff --git a/components/engine/opts/mount_test.go b/components/engine/opts/mount_test.go deleted file mode 100644 index 72aaa6258e..0000000000 --- a/components/engine/opts/mount_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package opts - -import ( - "os" - "testing" - - mounttypes "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/pkg/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMountOptString(t *testing.T) { - mount := MountOpt{ - values: []mounttypes.Mount{ - { - Type: mounttypes.TypeBind, - Source: "/home/path", - Target: "/target", - }, - { - Type: mounttypes.TypeVolume, - Source: "foo", - Target: "/target/foo", - }, - }, - } - expected := "bind /home/path /target, volume foo /target/foo" - assert.Equal(t, expected, mount.String()) -} - -func TestMountOptSetBindNoErrorBind(t *testing.T) { - for _, testcase := range []string{ - // tests several aliases that should have same result. - "type=bind,target=/target,source=/source", - "type=bind,src=/source,dst=/target", - "type=bind,source=/source,dst=/target", - "type=bind,src=/source,target=/target", - } { - var mount MountOpt - - assert.NoError(t, mount.Set(testcase)) - - mounts := mount.Value() - require.Len(t, mounts, 1) - assert.Equal(t, mounttypes.Mount{ - Type: mounttypes.TypeBind, - Source: "/source", - Target: "/target", - }, mounts[0]) - } -} - -func TestMountOptSetVolumeNoError(t *testing.T) { - for _, testcase := range []string{ - // tests several aliases that should have same result. - "type=volume,target=/target,source=/source", - "type=volume,src=/source,dst=/target", - "type=volume,source=/source,dst=/target", - "type=volume,src=/source,target=/target", - } { - var mount MountOpt - - assert.NoError(t, mount.Set(testcase)) - - mounts := mount.Value() - require.Len(t, mounts, 1) - assert.Equal(t, mounttypes.Mount{ - Type: mounttypes.TypeVolume, - Source: "/source", - Target: "/target", - }, mounts[0]) - } -} - -// TestMountOptDefaultType ensures that a mount without the type defaults to a -// volume mount. -func TestMountOptDefaultType(t *testing.T) { - var mount MountOpt - assert.NoError(t, mount.Set("target=/target,source=/foo")) - assert.Equal(t, mounttypes.TypeVolume, mount.values[0].Type) -} - -func TestMountOptSetErrorNoTarget(t *testing.T) { - var mount MountOpt - assert.EqualError(t, mount.Set("type=volume,source=/foo"), "target is required") -} - -func TestMountOptSetErrorInvalidKey(t *testing.T) { - var mount MountOpt - assert.EqualError(t, mount.Set("type=volume,bogus=foo"), "unexpected key 'bogus' in 'bogus=foo'") -} - -func TestMountOptSetErrorInvalidField(t *testing.T) { - var mount MountOpt - assert.EqualError(t, mount.Set("type=volume,bogus"), "invalid field 'bogus' must be a key=value pair") -} - -func TestMountOptSetErrorInvalidReadOnly(t *testing.T) { - var mount MountOpt - assert.EqualError(t, mount.Set("type=volume,readonly=no"), "invalid value for readonly: no") - assert.EqualError(t, mount.Set("type=volume,readonly=invalid"), "invalid value for readonly: invalid") -} - -func TestMountOptDefaultEnableReadOnly(t *testing.T) { - var m MountOpt - assert.NoError(t, m.Set("type=bind,target=/foo,source=/foo")) - assert.False(t, m.values[0].ReadOnly) - - m = MountOpt{} - assert.NoError(t, m.Set("type=bind,target=/foo,source=/foo,readonly")) - assert.True(t, m.values[0].ReadOnly) - - m = MountOpt{} - assert.NoError(t, m.Set("type=bind,target=/foo,source=/foo,readonly=1")) - assert.True(t, m.values[0].ReadOnly) - - m = MountOpt{} - assert.NoError(t, m.Set("type=bind,target=/foo,source=/foo,readonly=true")) - assert.True(t, m.values[0].ReadOnly) - - m = MountOpt{} - assert.NoError(t, m.Set("type=bind,target=/foo,source=/foo,readonly=0")) - assert.False(t, m.values[0].ReadOnly) -} - -func TestMountOptVolumeNoCopy(t *testing.T) { - var m MountOpt - assert.NoError(t, m.Set("type=volume,target=/foo,volume-nocopy")) - assert.Equal(t, "", m.values[0].Source) - - m = MountOpt{} - assert.NoError(t, m.Set("type=volume,target=/foo,source=foo")) - assert.True(t, m.values[0].VolumeOptions == nil) - - m = MountOpt{} - assert.NoError(t, m.Set("type=volume,target=/foo,source=foo,volume-nocopy=true")) - assert.True(t, m.values[0].VolumeOptions != nil) - assert.True(t, m.values[0].VolumeOptions.NoCopy) - - m = MountOpt{} - assert.NoError(t, m.Set("type=volume,target=/foo,source=foo,volume-nocopy")) - assert.True(t, m.values[0].VolumeOptions != nil) - assert.True(t, m.values[0].VolumeOptions.NoCopy) - - m = MountOpt{} - assert.NoError(t, m.Set("type=volume,target=/foo,source=foo,volume-nocopy=1")) - assert.True(t, m.values[0].VolumeOptions != nil) - assert.True(t, m.values[0].VolumeOptions.NoCopy) -} - -func TestMountOptTypeConflict(t *testing.T) { - var m MountOpt - testutil.ErrorContains(t, m.Set("type=bind,target=/foo,source=/foo,volume-nocopy=true"), "cannot mix") - testutil.ErrorContains(t, m.Set("type=volume,target=/foo,source=/foo,bind-propagation=rprivate"), "cannot mix") -} - -func TestMountOptSetTmpfsNoError(t *testing.T) { - for _, testcase := range []string{ - // tests several aliases that should have same result. - "type=tmpfs,target=/target,tmpfs-size=1m,tmpfs-mode=0700", - "type=tmpfs,target=/target,tmpfs-size=1MB,tmpfs-mode=700", - } { - var mount MountOpt - - assert.NoError(t, mount.Set(testcase)) - - mounts := mount.Value() - require.Len(t, mounts, 1) - assert.Equal(t, mounttypes.Mount{ - Type: mounttypes.TypeTmpfs, - Target: "/target", - TmpfsOptions: &mounttypes.TmpfsOptions{ - SizeBytes: 1024 * 1024, // not 1000 * 1000 - Mode: os.FileMode(0700), - }, - }, mounts[0]) - } -} - -func TestMountOptSetTmpfsError(t *testing.T) { - var m MountOpt - testutil.ErrorContains(t, m.Set("type=tmpfs,target=/foo,tmpfs-size=foo"), "invalid value for tmpfs-size") - testutil.ErrorContains(t, m.Set("type=tmpfs,target=/foo,tmpfs-mode=foo"), "invalid value for tmpfs-mode") - testutil.ErrorContains(t, m.Set("type=tmpfs"), "target is required") -} diff --git a/components/engine/opts/port.go b/components/engine/opts/port.go deleted file mode 100644 index 152683c981..0000000000 --- a/components/engine/opts/port.go +++ /dev/null @@ -1,162 +0,0 @@ -package opts - -import ( - "encoding/csv" - "fmt" - "regexp" - "strconv" - "strings" - - "github.com/docker/docker/api/types/swarm" - "github.com/docker/go-connections/nat" -) - -const ( - portOptTargetPort = "target" - portOptPublishedPort = "published" - portOptProtocol = "protocol" - portOptMode = "mode" -) - -// PortOpt represents a port config in swarm mode. -type PortOpt struct { - ports []swarm.PortConfig -} - -// Set a new port value -func (p *PortOpt) Set(value string) error { - longSyntax, err := regexp.MatchString(`\w+=\w+(,\w+=\w+)*`, value) - if err != nil { - return err - } - if longSyntax { - csvReader := csv.NewReader(strings.NewReader(value)) - fields, err := csvReader.Read() - if err != nil { - return err - } - - pConfig := swarm.PortConfig{} - for _, field := range fields { - parts := strings.SplitN(field, "=", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid field %s", field) - } - - key := strings.ToLower(parts[0]) - value := strings.ToLower(parts[1]) - - switch key { - case portOptProtocol: - if value != string(swarm.PortConfigProtocolTCP) && value != string(swarm.PortConfigProtocolUDP) { - return fmt.Errorf("invalid protocol value %s", value) - } - - pConfig.Protocol = swarm.PortConfigProtocol(value) - case portOptMode: - if value != string(swarm.PortConfigPublishModeIngress) && value != string(swarm.PortConfigPublishModeHost) { - return fmt.Errorf("invalid publish mode value %s", value) - } - - pConfig.PublishMode = swarm.PortConfigPublishMode(value) - case portOptTargetPort: - tPort, err := strconv.ParseUint(value, 10, 16) - if err != nil { - return err - } - - pConfig.TargetPort = uint32(tPort) - case portOptPublishedPort: - pPort, err := strconv.ParseUint(value, 10, 16) - if err != nil { - return err - } - - pConfig.PublishedPort = uint32(pPort) - default: - return fmt.Errorf("invalid field key %s", key) - } - } - - if pConfig.TargetPort == 0 { - return fmt.Errorf("missing mandatory field %q", portOptTargetPort) - } - - if pConfig.PublishMode == "" { - pConfig.PublishMode = swarm.PortConfigPublishModeIngress - } - - if pConfig.Protocol == "" { - pConfig.Protocol = swarm.PortConfigProtocolTCP - } - - p.ports = append(p.ports, pConfig) - } else { - // short syntax - portConfigs := []swarm.PortConfig{} - ports, portBindingMap, err := nat.ParsePortSpecs([]string{value}) - if err != nil { - return err - } - for _, portBindings := range portBindingMap { - for _, portBinding := range portBindings { - if portBinding.HostIP != "" { - return fmt.Errorf("HostIP is not supported.") - } - } - } - - for port := range ports { - portConfig, err := ConvertPortToPortConfig(port, portBindingMap) - if err != nil { - return err - } - portConfigs = append(portConfigs, portConfig...) - } - p.ports = append(p.ports, portConfigs...) - } - return nil -} - -// Type returns the type of this option -func (p *PortOpt) Type() string { - return "port" -} - -// String returns a string repr of this option -func (p *PortOpt) String() string { - ports := []string{} - for _, port := range p.ports { - repr := fmt.Sprintf("%v:%v/%s/%s", port.PublishedPort, port.TargetPort, port.Protocol, port.PublishMode) - ports = append(ports, repr) - } - return strings.Join(ports, ", ") -} - -// Value returns the ports -func (p *PortOpt) Value() []swarm.PortConfig { - return p.ports -} - -// ConvertPortToPortConfig converts ports to the swarm type -func ConvertPortToPortConfig( - port nat.Port, - portBindings map[nat.Port][]nat.PortBinding, -) ([]swarm.PortConfig, error) { - ports := []swarm.PortConfig{} - - for _, binding := range portBindings[port] { - hostPort, err := strconv.ParseUint(binding.HostPort, 10, 16) - if err != nil && binding.HostPort != "" { - return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port()) - } - ports = append(ports, swarm.PortConfig{ - //TODO Name: ? - Protocol: swarm.PortConfigProtocol(strings.ToLower(port.Proto())), - TargetPort: uint32(port.Int()), - PublishedPort: uint32(hostPort), - PublishMode: swarm.PortConfigPublishModeIngress, - }) - } - return ports, nil -} diff --git a/components/engine/opts/port_test.go b/components/engine/opts/port_test.go deleted file mode 100644 index 9315b7218c..0000000000 --- a/components/engine/opts/port_test.go +++ /dev/null @@ -1,296 +0,0 @@ -package opts - -import ( - "testing" - - "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/pkg/testutil" - "github.com/stretchr/testify/assert" -) - -func TestPortOptValidSimpleSyntax(t *testing.T) { - testCases := []struct { - value string - expected []swarm.PortConfig - }{ - { - value: "80", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "80:8080", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 8080, - PublishedPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "8080:80/tcp", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 80, - PublishedPort: 8080, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "80:8080/udp", - expected: []swarm.PortConfig{ - { - Protocol: "udp", - TargetPort: 8080, - PublishedPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "80-81:8080-8081/tcp", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 8080, - PublishedPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - { - Protocol: "tcp", - TargetPort: 8081, - PublishedPort: 81, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "80-82:8080-8082/udp", - expected: []swarm.PortConfig{ - { - Protocol: "udp", - TargetPort: 8080, - PublishedPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - { - Protocol: "udp", - TargetPort: 8081, - PublishedPort: 81, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - { - Protocol: "udp", - TargetPort: 8082, - PublishedPort: 82, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - } - for _, tc := range testCases { - var port PortOpt - assert.NoError(t, port.Set(tc.value)) - assert.Len(t, port.Value(), len(tc.expected)) - for _, expectedPortConfig := range tc.expected { - assertContains(t, port.Value(), expectedPortConfig) - } - } -} - -func TestPortOptValidComplexSyntax(t *testing.T) { - testCases := []struct { - value string - expected []swarm.PortConfig - }{ - { - value: "target=80", - expected: []swarm.PortConfig{ - { - TargetPort: 80, - Protocol: "tcp", - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "target=80,protocol=tcp", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "target=80,published=8080,protocol=tcp", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 80, - PublishedPort: 8080, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "published=80,target=8080,protocol=tcp", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 8080, - PublishedPort: 80, - PublishMode: swarm.PortConfigPublishModeIngress, - }, - }, - }, - { - value: "target=80,published=8080,protocol=tcp,mode=host", - expected: []swarm.PortConfig{ - { - Protocol: "tcp", - TargetPort: 80, - PublishedPort: 8080, - PublishMode: "host", - }, - }, - }, - { - value: "target=80,published=8080,mode=host", - expected: []swarm.PortConfig{ - { - TargetPort: 80, - PublishedPort: 8080, - PublishMode: "host", - Protocol: "tcp", - }, - }, - }, - { - value: "target=80,published=8080,mode=ingress", - expected: []swarm.PortConfig{ - { - TargetPort: 80, - PublishedPort: 8080, - PublishMode: "ingress", - Protocol: "tcp", - }, - }, - }, - } - for _, tc := range testCases { - var port PortOpt - assert.NoError(t, port.Set(tc.value)) - assert.Len(t, port.Value(), len(tc.expected)) - for _, expectedPortConfig := range tc.expected { - assertContains(t, port.Value(), expectedPortConfig) - } - } -} - -func TestPortOptInvalidComplexSyntax(t *testing.T) { - testCases := []struct { - value string - expectedError string - }{ - { - value: "invalid,target=80", - expectedError: "invalid field", - }, - { - value: "invalid=field", - expectedError: "invalid field", - }, - { - value: "protocol=invalid", - expectedError: "invalid protocol value", - }, - { - value: "target=invalid", - expectedError: "invalid syntax", - }, - { - value: "published=invalid", - expectedError: "invalid syntax", - }, - { - value: "mode=invalid", - expectedError: "invalid publish mode value", - }, - { - value: "published=8080,protocol=tcp,mode=ingress", - expectedError: "missing mandatory field", - }, - { - value: `target=80,protocol="tcp,mode=ingress"`, - expectedError: "non-quoted-field", - }, - { - value: `target=80,"protocol=tcp,mode=ingress"`, - expectedError: "invalid protocol value", - }, - } - for _, tc := range testCases { - var port PortOpt - testutil.ErrorContains(t, port.Set(tc.value), tc.expectedError) - } -} - -func TestPortOptInvalidSimpleSyntax(t *testing.T) { - testCases := []struct { - value string - expectedError string - }{ - { - value: "9999999", - expectedError: "Invalid containerPort: 9999999", - }, - { - value: "80/xyz", - expectedError: "Invalid proto: xyz", - }, - { - value: "tcp", - expectedError: "Invalid containerPort: tcp", - }, - { - value: "udp", - expectedError: "Invalid containerPort: udp", - }, - { - value: "", - expectedError: "No port specified: ", - }, - { - value: "1.1.1.1:80:80", - expectedError: "HostIP is not supported.", - }, - } - for _, tc := range testCases { - var port PortOpt - assert.EqualError(t, port.Set(tc.value), tc.expectedError) - } -} - -func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) { - var contains = false - for _, portConfig := range portConfigs { - if portConfig == expected { - contains = true - break - } - } - if !contains { - t.Errorf("expected %v to contain %v, did not", portConfigs, expected) - } -} diff --git a/components/engine/opts/secret.go b/components/engine/opts/secret.go deleted file mode 100644 index a1fde54d91..0000000000 --- a/components/engine/opts/secret.go +++ /dev/null @@ -1,98 +0,0 @@ -package opts - -import ( - "encoding/csv" - "fmt" - "os" - "strconv" - "strings" - - swarmtypes "github.com/docker/docker/api/types/swarm" -) - -// SecretOpt is a Value type for parsing secrets -type SecretOpt struct { - values []*swarmtypes.SecretReference -} - -// Set a new secret value -func (o *SecretOpt) Set(value string) error { - csvReader := csv.NewReader(strings.NewReader(value)) - fields, err := csvReader.Read() - if err != nil { - return err - } - - options := &swarmtypes.SecretReference{ - File: &swarmtypes.SecretReferenceFileTarget{ - UID: "0", - GID: "0", - Mode: 0444, - }, - } - - // support a simple syntax of --secret foo - if len(fields) == 1 { - options.File.Name = fields[0] - options.SecretName = fields[0] - o.values = append(o.values, options) - return nil - } - - for _, field := range fields { - parts := strings.SplitN(field, "=", 2) - key := strings.ToLower(parts[0]) - - if len(parts) != 2 { - return fmt.Errorf("invalid field '%s' must be a key=value pair", field) - } - - value := parts[1] - switch key { - case "source", "src": - options.SecretName = value - case "target": - options.File.Name = value - case "uid": - options.File.UID = value - case "gid": - options.File.GID = value - case "mode": - m, err := strconv.ParseUint(value, 0, 32) - if err != nil { - return fmt.Errorf("invalid mode specified: %v", err) - } - - options.File.Mode = os.FileMode(m) - default: - return fmt.Errorf("invalid field in secret request: %s", key) - } - } - - if options.SecretName == "" { - return fmt.Errorf("source is required") - } - - o.values = append(o.values, options) - return nil -} - -// Type returns the type of this option -func (o *SecretOpt) Type() string { - return "secret" -} - -// String returns a string repr of this option -func (o *SecretOpt) String() string { - secrets := []string{} - for _, secret := range o.values { - repr := fmt.Sprintf("%s -> %s", secret.SecretName, secret.File.Name) - secrets = append(secrets, repr) - } - return strings.Join(secrets, ", ") -} - -// Value returns the secret requests -func (o *SecretOpt) Value() []*swarmtypes.SecretReference { - return o.values -} diff --git a/components/engine/opts/secret_test.go b/components/engine/opts/secret_test.go deleted file mode 100644 index ad0005e4ad..0000000000 --- a/components/engine/opts/secret_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package opts - -import ( - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSecretOptionsSimple(t *testing.T) { - var opt SecretOpt - - testCase := "app-secret" - assert.NoError(t, opt.Set(testCase)) - - reqs := opt.Value() - require.Len(t, reqs, 1) - req := reqs[0] - assert.Equal(t, "app-secret", req.SecretName) - assert.Equal(t, "app-secret", req.File.Name) - assert.Equal(t, "0", req.File.UID) - assert.Equal(t, "0", req.File.GID) -} - -func TestSecretOptionsSourceTarget(t *testing.T) { - var opt SecretOpt - - testCase := "source=foo,target=testing" - assert.NoError(t, opt.Set(testCase)) - - reqs := opt.Value() - require.Len(t, reqs, 1) - req := reqs[0] - assert.Equal(t, "foo", req.SecretName) - assert.Equal(t, "testing", req.File.Name) -} - -func TestSecretOptionsShorthand(t *testing.T) { - var opt SecretOpt - - testCase := "src=foo,target=testing" - assert.NoError(t, opt.Set(testCase)) - - reqs := opt.Value() - require.Len(t, reqs, 1) - req := reqs[0] - assert.Equal(t, "foo", req.SecretName) -} - -func TestSecretOptionsCustomUidGid(t *testing.T) { - var opt SecretOpt - - testCase := "source=foo,target=testing,uid=1000,gid=1001" - assert.NoError(t, opt.Set(testCase)) - - reqs := opt.Value() - require.Len(t, reqs, 1) - req := reqs[0] - assert.Equal(t, "foo", req.SecretName) - assert.Equal(t, "testing", req.File.Name) - assert.Equal(t, "1000", req.File.UID) - assert.Equal(t, "1001", req.File.GID) -} - -func TestSecretOptionsCustomMode(t *testing.T) { - var opt SecretOpt - - testCase := "source=foo,target=testing,uid=1000,gid=1001,mode=0444" - assert.NoError(t, opt.Set(testCase)) - - reqs := opt.Value() - require.Len(t, reqs, 1) - req := reqs[0] - assert.Equal(t, "foo", req.SecretName) - assert.Equal(t, "testing", req.File.Name) - assert.Equal(t, "1000", req.File.UID) - assert.Equal(t, "1001", req.File.GID) - assert.Equal(t, os.FileMode(0444), req.File.Mode) -} diff --git a/components/engine/opts/weightdevice.go b/components/engine/opts/weightdevice.go deleted file mode 100644 index 7e3d064f27..0000000000 --- a/components/engine/opts/weightdevice.go +++ /dev/null @@ -1,89 +0,0 @@ -package opts - -import ( - "fmt" - "strconv" - "strings" - - "github.com/docker/docker/api/types/blkiodev" -) - -// ValidatorWeightFctType defines a validator function that returns a validated struct and/or an error. -type ValidatorWeightFctType func(val string) (*blkiodev.WeightDevice, error) - -// ValidateWeightDevice validates that the specified string has a valid device-weight format. -func ValidateWeightDevice(val string) (*blkiodev.WeightDevice, error) { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - weight, err := strconv.ParseUint(split[1], 10, 0) - if err != nil { - return nil, fmt.Errorf("invalid weight for device: %s", val) - } - if weight > 0 && (weight < 10 || weight > 1000) { - return nil, fmt.Errorf("invalid weight for device: %s", val) - } - - return &blkiodev.WeightDevice{ - Path: split[0], - Weight: uint16(weight), - }, nil -} - -// WeightdeviceOpt defines a map of WeightDevices -type WeightdeviceOpt struct { - values []*blkiodev.WeightDevice - validator ValidatorWeightFctType -} - -// NewWeightdeviceOpt creates a new WeightdeviceOpt -func NewWeightdeviceOpt(validator ValidatorWeightFctType) WeightdeviceOpt { - values := []*blkiodev.WeightDevice{} - return WeightdeviceOpt{ - values: values, - validator: validator, - } -} - -// Set validates a WeightDevice and sets its name as a key in WeightdeviceOpt -func (opt *WeightdeviceOpt) Set(val string) error { - var value *blkiodev.WeightDevice - if opt.validator != nil { - v, err := opt.validator(val) - if err != nil { - return err - } - value = v - } - (opt.values) = append((opt.values), value) - return nil -} - -// String returns WeightdeviceOpt values as a string. -func (opt *WeightdeviceOpt) String() string { - var out []string - for _, v := range opt.values { - out = append(out, v.String()) - } - - return fmt.Sprintf("%v", out) -} - -// GetList returns a slice of pointers to WeightDevices. -func (opt *WeightdeviceOpt) GetList() []*blkiodev.WeightDevice { - var weightdevice []*blkiodev.WeightDevice - for _, v := range opt.values { - weightdevice = append(weightdevice, v) - } - - return weightdevice -} - -// Type returns the option type -func (opt *WeightdeviceOpt) Type() string { - return "list" -}