Browse code

Merge pull request #29819 from vdemeester/service-validate-publish-simple-syntax

Make sure we validate simple syntax on service commands

Alexander Morozov authored on 2017/02/16 05:31:46
Showing 5 changed files
... ...
@@ -10,7 +10,6 @@ import (
10 10
 	"github.com/docker/docker/api/types/swarm"
11 11
 	"github.com/docker/docker/opts"
12 12
 	runconfigopts "github.com/docker/docker/runconfig/opts"
13
-	"github.com/docker/go-connections/nat"
14 13
 	"github.com/spf13/cobra"
15 14
 )
16 15
 
... ...
@@ -244,17 +243,6 @@ func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error
244 244
 	return healthConfig, nil
245 245
 }
246 246
 
247
-// ValidatePort validates a string is in the expected format for a port definition
248
-func ValidatePort(value string) (string, error) {
249
-	portMappings, err := nat.ParsePortSpec(value)
250
-	for _, portMapping := range portMappings {
251
-		if portMapping.Binding.HostIP != "" {
252
-			return "", fmt.Errorf("HostIP is not supported by a service.")
253
-		}
254
-	}
255
-	return value, err
256
-}
257
-
258 247
 // convertExtraHostsToSwarmHosts converts an array of extra hosts in cli
259 248
 //     <host>:<ip>
260 249
 // into a swarmkit host format:
... ...
@@ -663,24 +663,6 @@ func portConfigToString(portConfig *swarm.PortConfig) string {
663 663
 	return fmt.Sprintf("%v:%v/%s/%s", portConfig.PublishedPort, portConfig.TargetPort, protocol, mode)
664 664
 }
665 665
 
666
-// FIXME(vdemeester) port to opts.PortOpt
667
-// This validation is only used for `--publish-rm`.
668
-// The `--publish-rm` takes:
669
-// <TargetPort>[/<Protocol>] (e.g., 80, 80/tcp, 53/udp)
670
-func validatePublishRemove(val string) (string, error) {
671
-	proto, port := nat.SplitProtoPort(val)
672
-	if proto != "tcp" && proto != "udp" {
673
-		return "", fmt.Errorf("invalid protocol '%s' for %s", proto, val)
674
-	}
675
-	if strings.Contains(port, ":") {
676
-		return "", fmt.Errorf("invalid port format: '%s', should be <TargetPort>[/<Protocol>] (e.g., 80, 80/tcp, 53/udp)", port)
677
-	}
678
-	if _, err := nat.ParsePort(port); err != nil {
679
-		return "", err
680
-	}
681
-	return val, nil
682
-}
683
-
684 666
 func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) error {
685 667
 	// The key of the map is `port/protocol`, e.g., `80/tcp`
686 668
 	portSet := map[string]swarm.PortConfig{}
... ...
@@ -362,29 +362,6 @@ func TestUpdatePortsRmWithProtocol(t *testing.T) {
362 362
 	assert.Equal(t, portConfigs[1].TargetPort, uint32(82))
363 363
 }
364 364
 
365
-// FIXME(vdemeester) port to opts.PortOpt
366
-func TestValidatePort(t *testing.T) {
367
-	validPorts := []string{"80/tcp", "80", "80/udp"}
368
-	invalidPorts := map[string]string{
369
-		"9999999":   "out of range",
370
-		"80:80/tcp": "invalid port format",
371
-		"53:53/udp": "invalid port format",
372
-		"80:80":     "invalid port format",
373
-		"80/xyz":    "invalid protocol",
374
-		"tcp":       "invalid syntax",
375
-		"udp":       "invalid syntax",
376
-		"":          "invalid protocol",
377
-	}
378
-	for _, port := range validPorts {
379
-		_, err := validatePublishRemove(port)
380
-		assert.Equal(t, err, nil)
381
-	}
382
-	for port, e := range invalidPorts {
383
-		_, err := validatePublishRemove(port)
384
-		assert.Error(t, err, e)
385
-	}
386
-}
387
-
388 365
 type secretAPIClientMock struct {
389 366
 	listResult []swarm.Secret
390 367
 }
... ...
@@ -94,11 +94,20 @@ func (p *PortOpt) Set(value string) error {
94 94
 	} else {
95 95
 		// short syntax
96 96
 		portConfigs := []swarm.PortConfig{}
97
-		// We can ignore errors because the format was already validated by ValidatePort
98
-		ports, portBindings, _ := nat.ParsePortSpecs([]string{value})
97
+		ports, portBindingMap, err := nat.ParsePortSpecs([]string{value})
98
+		if err != nil {
99
+			return err
100
+		}
101
+		for _, portBindings := range portBindingMap {
102
+			for _, portBinding := range portBindings {
103
+				if portBinding.HostIP != "" {
104
+					return fmt.Errorf("HostIP is not supported.")
105
+				}
106
+			}
107
+		}
99 108
 
100 109
 		for port := range ports {
101
-			portConfig, err := ConvertPortToPortConfig(port, portBindings)
110
+			portConfig, err := ConvertPortToPortConfig(port, portBindingMap)
102 111
 			if err != nil {
103 112
 				return err
104 113
 			}
... ...
@@ -245,6 +245,42 @@ func TestPortOptInvalidComplexSyntax(t *testing.T) {
245 245
 	}
246 246
 }
247 247
 
248
+func TestPortOptInvalidSimpleSyntax(t *testing.T) {
249
+	testCases := []struct {
250
+		value         string
251
+		expectedError string
252
+	}{
253
+		{
254
+			value:         "9999999",
255
+			expectedError: "Invalid containerPort: 9999999",
256
+		},
257
+		{
258
+			value:         "80/xyz",
259
+			expectedError: "Invalid proto: xyz",
260
+		},
261
+		{
262
+			value:         "tcp",
263
+			expectedError: "Invalid containerPort: tcp",
264
+		},
265
+		{
266
+			value:         "udp",
267
+			expectedError: "Invalid containerPort: udp",
268
+		},
269
+		{
270
+			value:         "",
271
+			expectedError: "No port specified",
272
+		},
273
+		{
274
+			value:         "1.1.1.1:80:80",
275
+			expectedError: "HostIP is not supported",
276
+		},
277
+	}
278
+	for _, tc := range testCases {
279
+		var port PortOpt
280
+		assert.Error(t, port.Set(tc.value), tc.expectedError)
281
+	}
282
+}
283
+
248 284
 func assertContains(t *testing.T, portConfigs []swarm.PortConfig, expected swarm.PortConfig) {
249 285
 	var contains = false
250 286
 	for _, portConfig := range portConfigs {