Skip to content

Commit

Permalink
Merge pull request moby#30476 from yongtang/30447-port-config-long-sy…
Browse files Browse the repository at this point in the history
…ntax

Support expanded syntax of ports in `docker stack deploy`
  • Loading branch information
thaJeztah authored Feb 11, 2017
2 parents 9e940b9 + c534712 commit ad5f808
Show file tree
Hide file tree
Showing 7 changed files with 366 additions and 28 deletions.
22 changes: 9 additions & 13 deletions compose/convert/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package convert
import (
"fmt"
"os"
"sort"
"time"

"github.com/docker/docker/api/types"
Expand All @@ -13,8 +14,6 @@ import (
"github.com/docker/docker/client"
"github.com/docker/docker/opts"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/go-connections/nat"
"sort"
)

// Services from compose-file types to engine API types
Expand Down Expand Up @@ -367,19 +366,16 @@ func (a byPublishedPort) Len() int { return len(a) }
func (a byPublishedPort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byPublishedPort) Less(i, j int) bool { return a[i].PublishedPort < a[j].PublishedPort }

func convertEndpointSpec(source []string) (*swarm.EndpointSpec, error) {
func convertEndpointSpec(source []composetypes.ServicePortConfig) (*swarm.EndpointSpec, error) {
portConfigs := []swarm.PortConfig{}
ports, portBindings, err := nat.ParsePortSpecs(source)
if err != nil {
return nil, err
}

for port := range ports {
portConfig, err := opts.ConvertPortToPortConfig(port, portBindings)
if err != nil {
return nil, err
for _, port := range source {
portConfig := swarm.PortConfig{
Protocol: swarm.PortConfigProtocol(port.Protocol),
TargetPort: port.Target,
PublishedPort: port.Published,
PublishMode: swarm.PortConfigPublishMode(port.Mode),
}
portConfigs = append(portConfigs, portConfig...)
portConfigs = append(portConfigs, portConfig)
}

sort.Sort(byPublishedPort(portConfigs))
Expand Down
34 changes: 34 additions & 0 deletions compose/convert/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,40 @@ func TestConvertHealthcheckDisableWithTest(t *testing.T) {
assert.Error(t, err, "test and disable can't be set")
}

func TestConvertEndpointSpec(t *testing.T) {
source := []composetypes.ServicePortConfig{
{
Protocol: "udp",
Target: 53,
Published: 1053,
Mode: "host",
},
{
Target: 8080,
Published: 80,
},
}
endpoint, err := convertEndpointSpec(source)

expected := swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
TargetPort: 8080,
PublishedPort: 80,
},
{
Protocol: "udp",
TargetPort: 53,
PublishedPort: 1053,
PublishMode: "host",
},
},
}

assert.NilError(t, err)
assert.DeepEqual(t, *endpoint, expected)
}

func TestConvertServiceNetworksOnlyDefault(t *testing.T) {
networkConfigs := networkMap{}
networks := map[string]*composetypes.ServiceNetworkConfig{}
Expand Down
78 changes: 75 additions & 3 deletions compose/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (
"github.com/docker/docker/cli/compose/interpolation"
"github.com/docker/docker/cli/compose/schema"
"github.com/docker/docker/cli/compose/types"
"github.com/docker/docker/runconfig/opts"
"github.com/docker/docker/opts"
runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/go-connections/nat"
units "github.com/docker/go-units"
shellwords "github.com/mattn/go-shellwords"
"github.com/mitchellh/mapstructure"
Expand Down Expand Up @@ -237,6 +239,8 @@ func transformHook(
return transformUlimits(data)
case reflect.TypeOf(types.UnitBytes(0)):
return transformSize(data)
case reflect.TypeOf([]types.ServicePortConfig{}):
return transformServicePort(data)
case reflect.TypeOf(types.ServiceSecretConfig{}):
return transformServiceSecret(data)
case reflect.TypeOf(types.StringOrNumberList{}):
Expand Down Expand Up @@ -340,14 +344,14 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string) e

for _, file := range serviceConfig.EnvFile {
filePath := absPath(workingDir, file)
fileVars, err := opts.ParseEnvFile(filePath)
fileVars, err := runconfigopts.ParseEnvFile(filePath)
if err != nil {
return err
}
envVars = append(envVars, fileVars...)
}

for k, v := range opts.ConvertKVStringsToMap(envVars) {
for k, v := range runconfigopts.ConvertKVStringsToMap(envVars) {
environment[k] = v
}
}
Expand Down Expand Up @@ -481,6 +485,41 @@ func transformExternal(data interface{}) (interface{}, error) {
}
}

func transformServicePort(data interface{}) (interface{}, error) {
switch entries := data.(type) {
case []interface{}:
// We process the list instead of individual items here.
// The reason is that one entry might be mapped to multiple ServicePortConfig.
// Therefore we take an input of a list and return an output of a list.
ports := []interface{}{}
for _, entry := range entries {
switch value := entry.(type) {
case int:
v, err := toServicePortConfigs(fmt.Sprint(value))
if err != nil {
return data, err
}
ports = append(ports, v...)
case string:
v, err := toServicePortConfigs(value)
if err != nil {
return data, err
}
ports = append(ports, v...)
case types.Dict:
ports = append(ports, value)
case map[string]interface{}:
ports = append(ports, value)
default:
return data, fmt.Errorf("invalid type %T for port", value)
}
}
return ports, nil
default:
return data, fmt.Errorf("invalid type %T for port", entries)
}
}

func transformServiceSecret(data interface{}) (interface{}, error) {
switch value := data.(type) {
case string:
Expand Down Expand Up @@ -572,6 +611,39 @@ func transformSize(value interface{}) (int64, error) {
panic(fmt.Errorf("invalid type for size %T", value))
}

func toServicePortConfigs(value string) ([]interface{}, error) {
var portConfigs []interface{}

ports, portBindings, err := nat.ParsePortSpecs([]string{value})
if err != nil {
return nil, err
}
// We need to sort the key of the ports to make sure it is consistent
keys := []string{}
for port := range ports {
keys = append(keys, string(port))
}
sort.Strings(keys)

for _, key := range keys {
// Reuse ConvertPortToPortConfig so that it is consistent
portConfig, err := opts.ConvertPortToPortConfig(nat.Port(key), portBindings)
if err != nil {
return nil, err
}
for _, p := range portConfig {
portConfigs = append(portConfigs, types.ServicePortConfig{
Protocol: string(p.Protocol),
Target: p.TargetPort,
Published: p.PublishedPort,
Mode: string(p.PublishMode),
})
}
}

return portConfigs, nil
}

func toMapStringString(value map[string]interface{}) map[string]string {
output := make(map[string]string)
for key, value := range value {
Expand Down
Loading

0 comments on commit ad5f808

Please sign in to comment.