Remove --port and update --publish for services to support syntaxes

Add support for simple and complex syntax to `--publish` through the
use of `PortOpt`.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2016-12-08 22:32:10 +01:00
parent f099e134e4
commit 7fbc616b47
5 changed files with 37 additions and 102 deletions

View File

@ -40,13 +40,11 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.Var(&opts.networks, flagNetwork, "Network attachments") flags.Var(&opts.networks, flagNetwork, "Network attachments")
flags.Var(&opts.secrets, flagSecret, "Specify secrets to expose to the service") flags.Var(&opts.secrets, flagSecret, "Specify secrets to expose to the service")
flags.VarP(&opts.endpoint.publishPorts, flagPublish, "p", "Publish a port as a node port") flags.VarP(&opts.endpoint.publishPorts, flagPublish, "p", "Publish a port as a node port")
flags.MarkHidden(flagPublish)
flags.Var(&opts.groups, flagGroup, "Set one or more supplementary user groups for the container") flags.Var(&opts.groups, flagGroup, "Set one or more supplementary user groups for the container")
flags.Var(&opts.dns, flagDNS, "Set custom DNS servers") flags.Var(&opts.dns, flagDNS, "Set custom DNS servers")
flags.Var(&opts.dnsOption, flagDNSOption, "Set DNS options") flags.Var(&opts.dnsOption, flagDNSOption, "Set DNS options")
flags.Var(&opts.dnsSearch, flagDNSSearch, "Set custom DNS search domains") flags.Var(&opts.dnsSearch, flagDNSSearch, "Set custom DNS search domains")
flags.Var(&opts.hosts, flagHost, "Set one or more custom host-to-IP mappings (host:ip)") flags.Var(&opts.hosts, flagHost, "Set one or more custom host-to-IP mappings (host:ip)")
flags.Var(&opts.endpoint.expandedPorts, flagPort, "Publish a port")
flags.SetInterspersed(false) flags.SetInterspersed(false)
return cmd return cmd

View File

@ -288,44 +288,16 @@ func convertNetworks(networks []string) []swarm.NetworkAttachmentConfig {
type endpointOptions struct { type endpointOptions struct {
mode string mode string
publishPorts opts.ListOpts publishPorts opts.PortOpt
expandedPorts opts.PortOpt
} }
func (e *endpointOptions) ToEndpointSpec() *swarm.EndpointSpec { func (e *endpointOptions) ToEndpointSpec() *swarm.EndpointSpec {
portConfigs := []swarm.PortConfig{}
// We can ignore errors because the format was already validated by ValidatePort
ports, portBindings, _ := nat.ParsePortSpecs(e.publishPorts.GetAll())
for port := range ports {
portConfigs = append(portConfigs, ConvertPortToPortConfig(port, portBindings)...)
}
return &swarm.EndpointSpec{ return &swarm.EndpointSpec{
Mode: swarm.ResolutionMode(strings.ToLower(e.mode)), Mode: swarm.ResolutionMode(strings.ToLower(e.mode)),
Ports: append(portConfigs, e.expandedPorts.Value()...), Ports: e.publishPorts.Value(),
} }
} }
// ConvertPortToPortConfig converts ports to the swarm type
func ConvertPortToPortConfig(
port nat.Port,
portBindings map[nat.Port][]nat.PortBinding,
) []swarm.PortConfig {
ports := []swarm.PortConfig{}
for _, binding := range portBindings[port] {
hostPort, _ := strconv.ParseUint(binding.HostPort, 10, 16)
ports = append(ports, swarm.PortConfig{
//TODO Name: ?
Protocol: swarm.PortConfigProtocol(strings.ToLower(port.Proto())),
TargetPort: uint32(port.Int()),
PublishedPort: uint32(hostPort),
})
}
return ports
}
type logDriverOptions struct { type logDriverOptions struct {
name string name string
opts opts.ListOpts opts opts.ListOpts
@ -459,9 +431,6 @@ func newServiceOptions() *serviceOptions {
containerLabels: opts.NewListOpts(runconfigopts.ValidateEnv), containerLabels: opts.NewListOpts(runconfigopts.ValidateEnv),
env: opts.NewListOpts(runconfigopts.ValidateEnv), env: opts.NewListOpts(runconfigopts.ValidateEnv),
envFile: opts.NewListOpts(nil), envFile: opts.NewListOpts(nil),
endpoint: endpointOptions{
publishPorts: opts.NewListOpts(ValidatePort),
},
groups: opts.NewListOpts(nil), groups: opts.NewListOpts(nil),
logDriver: newLogDriverOptions(), logDriver: newLogDriverOptions(),
dns: opts.NewListOpts(opts.ValidateIPAddress), dns: opts.NewListOpts(opts.ValidateIPAddress),
@ -649,9 +618,6 @@ const (
flagPublish = "publish" flagPublish = "publish"
flagPublishRemove = "publish-rm" flagPublishRemove = "publish-rm"
flagPublishAdd = "publish-add" flagPublishAdd = "publish-add"
flagPort = "port"
flagPortAdd = "port-add"
flagPortRemove = "port-rm"
flagReplicas = "replicas" flagReplicas = "replicas"
flagReserveCPU = "reserve-cpu" flagReserveCPU = "reserve-cpu"
flagReserveMemory = "reserve-memory" flagReserveMemory = "reserve-memory"

View File

@ -24,7 +24,7 @@ import (
) )
func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command { func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
opts := newServiceOptions() serviceOpts := newServiceOptions()
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update [OPTIONS] SERVICE", Use: "update [OPTIONS] SERVICE",
@ -40,36 +40,33 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.String("args", "", "Service command args") flags.String("args", "", "Service command args")
flags.Bool("rollback", false, "Rollback to previous specification") flags.Bool("rollback", false, "Rollback to previous specification")
flags.Bool("force", false, "Force update even if no changes require it") flags.Bool("force", false, "Force update even if no changes require it")
addServiceFlags(cmd, opts) addServiceFlags(cmd, serviceOpts)
flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable") flags.Var(newListOptsVar(), flagEnvRemove, "Remove an environment variable")
flags.Var(newListOptsVar(), flagGroupRemove, "Remove a previously added supplementary user group from the container") flags.Var(newListOptsVar(), flagGroupRemove, "Remove a previously added supplementary user group from the container")
flags.Var(newListOptsVar(), flagLabelRemove, "Remove a label by its key") flags.Var(newListOptsVar(), flagLabelRemove, "Remove a label by its key")
flags.Var(newListOptsVar(), flagContainerLabelRemove, "Remove a container label by its key") flags.Var(newListOptsVar(), flagContainerLabelRemove, "Remove a container label by its key")
flags.Var(newListOptsVar(), flagMountRemove, "Remove a mount by its target path") flags.Var(newListOptsVar(), flagMountRemove, "Remove a mount by its target path")
flags.Var(newListOptsVar().WithValidator(validatePublishRemove), flagPublishRemove, "Remove a published port by its target port") // flags.Var(newListOptsVar().WithValidator(validatePublishRemove), flagPublishRemove, "Remove a published port by its target port")
flags.MarkHidden(flagPublishRemove) flags.Var(&opts.PortOpt{}, flagPublishRemove, "Remove a published port by its target port")
flags.Var(newListOptsVar(), flagPortRemove, "Remove a port(target-port mandatory)")
flags.Var(newListOptsVar(), flagConstraintRemove, "Remove a constraint") flags.Var(newListOptsVar(), flagConstraintRemove, "Remove a constraint")
flags.Var(newListOptsVar(), flagDNSRemove, "Remove a custom DNS server") flags.Var(newListOptsVar(), flagDNSRemove, "Remove a custom DNS server")
flags.Var(newListOptsVar(), flagDNSOptionRemove, "Remove a DNS option") flags.Var(newListOptsVar(), flagDNSOptionRemove, "Remove a DNS option")
flags.Var(newListOptsVar(), flagDNSSearchRemove, "Remove a DNS search domain") flags.Var(newListOptsVar(), flagDNSSearchRemove, "Remove a DNS search domain")
flags.Var(newListOptsVar(), flagHostRemove, "Remove a custom host-to-IP mapping (host:ip)") flags.Var(newListOptsVar(), flagHostRemove, "Remove a custom host-to-IP mapping (host:ip)")
flags.Var(&opts.labels, flagLabelAdd, "Add or update a service label") flags.Var(&serviceOpts.labels, flagLabelAdd, "Add or update a service label")
flags.Var(&opts.containerLabels, flagContainerLabelAdd, "Add or update a container label") flags.Var(&serviceOpts.containerLabels, flagContainerLabelAdd, "Add or update a container label")
flags.Var(&opts.env, flagEnvAdd, "Add or update an environment variable") flags.Var(&serviceOpts.env, flagEnvAdd, "Add or update an environment variable")
flags.Var(newListOptsVar(), flagSecretRemove, "Remove a secret") flags.Var(newListOptsVar(), flagSecretRemove, "Remove a secret")
flags.Var(&opts.secrets, flagSecretAdd, "Add or update a secret on a service") flags.Var(&serviceOpts.secrets, flagSecretAdd, "Add or update a secret on a service")
flags.Var(&opts.mounts, flagMountAdd, "Add or update a mount on a service") flags.Var(&serviceOpts.mounts, flagMountAdd, "Add or update a mount on a service")
flags.Var(&opts.constraints, flagConstraintAdd, "Add or update a placement constraint") flags.Var(&serviceOpts.constraints, flagConstraintAdd, "Add or update a placement constraint")
flags.Var(&opts.endpoint.publishPorts, flagPublishAdd, "Add or update a published port") flags.Var(&serviceOpts.endpoint.publishPorts, flagPublishAdd, "Add or update a published port")
flags.MarkHidden(flagPublishAdd) flags.Var(&serviceOpts.groups, flagGroupAdd, "Add an additional supplementary user group to the container")
flags.Var(&opts.endpoint.expandedPorts, flagPortAdd, "Add or update a port") flags.Var(&serviceOpts.dns, flagDNSAdd, "Add or update a custom DNS server")
flags.Var(&opts.groups, flagGroupAdd, "Add an additional supplementary user group to the container") flags.Var(&serviceOpts.dnsOption, flagDNSOptionAdd, "Add or update a DNS option")
flags.Var(&opts.dns, flagDNSAdd, "Add or update a custom DNS server") flags.Var(&serviceOpts.dnsSearch, flagDNSSearchAdd, "Add or update a custom DNS search domain")
flags.Var(&opts.dnsOption, flagDNSOptionAdd, "Add or update a DNS option") flags.Var(&serviceOpts.hosts, flagHostAdd, "Add or update a custom host-to-IP mapping (host:ip)")
flags.Var(&opts.dnsSearch, flagDNSSearchAdd, "Add or update a custom DNS search domain")
flags.Var(&opts.hosts, flagHostAdd, "Add or update a custom host-to-IP mapping (host:ip)")
return cmd return cmd
} }
@ -276,7 +273,7 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
} }
} }
if anyChanged(flags, flagPublishAdd, flagPublishRemove, flagPortAdd, flagPortRemove) { if anyChanged(flags, flagPublishAdd, flagPublishRemove) {
if spec.EndpointSpec == nil { if spec.EndpointSpec == nil {
spec.EndpointSpec = &swarm.EndpointSpec{} spec.EndpointSpec = &swarm.EndpointSpec{}
} }
@ -645,6 +642,7 @@ func portConfigToString(portConfig *swarm.PortConfig) string {
return fmt.Sprintf("%v:%v/%s/%s", portConfig.PublishedPort, portConfig.TargetPort, protocol, mode) return fmt.Sprintf("%v:%v/%s/%s", portConfig.PublishedPort, portConfig.TargetPort, protocol, mode)
} }
// FIXME(vdemeester) port to opts.PortOpt
// This validation is only used for `--publish-rm`. // This validation is only used for `--publish-rm`.
// The `--publish-rm` takes: // The `--publish-rm` takes:
// <TargetPort>[/<Protocol>] (e.g., 80, 80/tcp, 53/udp) // <TargetPort>[/<Protocol>] (e.g., 80, 80/tcp, 53/udp)
@ -667,26 +665,13 @@ func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) error {
portSet := map[string]swarm.PortConfig{} portSet := map[string]swarm.PortConfig{}
// Check to see if there are any conflict in flags. // Check to see if there are any conflict in flags.
if flags.Changed(flagPublishAdd) { if flags.Changed(flagPublishAdd) {
values := flags.Lookup(flagPublishAdd).Value.(*opts.ListOpts).GetAll() ports := flags.Lookup(flagPublishAdd).Value.(*opts.PortOpt).Value()
ports, portBindings, _ := nat.ParsePortSpecs(values)
for port := range ports { for _, port := range ports {
newConfigs := ConvertPortToPortConfig(port, portBindings) if v, ok := portSet[portConfigToString(&port)]; ok && v != port {
for _, entry := range newConfigs { return fmt.Errorf("conflicting port mapping between %v:%v/%s and %v:%v/%s", port.PublishedPort, port.TargetPort, port.Protocol, v.PublishedPort, v.TargetPort, v.Protocol)
if v, ok := portSet[portConfigToString(&entry)]; ok && v != entry {
return fmt.Errorf("conflicting port mapping between %v:%v/%s and %v:%v/%s", entry.PublishedPort, entry.TargetPort, entry.Protocol, v.PublishedPort, v.TargetPort, v.Protocol)
} }
portSet[portConfigToString(&entry)] = entry portSet[portConfigToString(&port)] = port
}
}
}
if flags.Changed(flagPortAdd) {
for _, entry := range flags.Lookup(flagPortAdd).Value.(*opts.PortOpt).Value() {
if v, ok := portSet[portConfigToString(&entry)]; ok && v != entry {
return fmt.Errorf("conflicting port mapping between %v:%v/%s and %v:%v/%s", entry.PublishedPort, entry.TargetPort, entry.Protocol, v.PublishedPort, v.TargetPort, v.Protocol)
}
portSet[portConfigToString(&entry)] = entry
} }
} }
@ -697,26 +682,12 @@ func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) error {
} }
} }
toRemove := flags.Lookup(flagPublishRemove).Value.(*opts.ListOpts).GetAll() toRemove := flags.Lookup(flagPublishRemove).Value.(*opts.PortOpt).Value()
removePortCSV := flags.Lookup(flagPortRemove).Value.(*opts.ListOpts).GetAll()
removePortOpts := &opts.PortOpt{}
for _, portCSV := range removePortCSV {
if err := removePortOpts.Set(portCSV); err != nil {
return err
}
}
newPorts := []swarm.PortConfig{} newPorts := []swarm.PortConfig{}
portLoop: portLoop:
for _, port := range portSet { for _, port := range portSet {
for _, rawTargetPort := range toRemove { for _, pConfig := range toRemove {
targetPort := nat.Port(rawTargetPort)
if equalPort(targetPort, port) {
continue portLoop
}
}
for _, pConfig := range removePortOpts.Value() {
if equalProtocol(port.Protocol, pConfig.Protocol) && if equalProtocol(port.Protocol, pConfig.Protocol) &&
port.TargetPort == pConfig.TargetPort && port.TargetPort == pConfig.TargetPort &&
equalPublishMode(port.PublishMode, pConfig.PublishMode) { equalPublishMode(port.PublishMode, pConfig.PublishMode) {

View File

@ -364,6 +364,7 @@ func TestUpdatePortsRmWithProtocol(t *testing.T) {
assert.Equal(t, portConfigs[0].TargetPort, uint32(82)) assert.Equal(t, portConfigs[0].TargetPort, uint32(82))
} }
// FIXME(vdemeester) port to opts.PortOpt
func TestValidatePort(t *testing.T) { func TestValidatePort(t *testing.T) {
validPorts := []string{"80/tcp", "80", "80/udp"} validPorts := []string{"80/tcp", "80", "80/udp"}
invalidPorts := map[string]string{ invalidPorts := map[string]string{

View File

@ -21,7 +21,6 @@ import (
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/cli" "github.com/docker/docker/cli"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
servicecmd "github.com/docker/docker/cli/command/service"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
"github.com/docker/docker/opts" "github.com/docker/docker/opts"
runconfigopts "github.com/docker/docker/runconfig/opts" runconfigopts "github.com/docker/docker/runconfig/opts"
@ -745,7 +744,7 @@ func convertEndpointSpec(source []string) (*swarm.EndpointSpec, error) {
for port := range ports { for port := range ports {
portConfigs = append( portConfigs = append(
portConfigs, portConfigs,
servicecmd.ConvertPortToPortConfig(port, portBindings)...) opts.ConvertPortToPortConfig(port, portBindings)...)
} }
return &swarm.EndpointSpec{Ports: portConfigs}, nil return &swarm.EndpointSpec{Ports: portConfigs}, nil