Signed-off-by: Alessandro Boch <aboch@docker.com>
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"github.com/docker/engine-api/client" |
| 14 | 14 |
"github.com/docker/engine-api/types" |
| 15 | 15 |
"github.com/docker/engine-api/types/container" |
| 16 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 16 | 17 |
) |
| 17 | 18 |
|
| 18 | 19 |
func (cli *DockerCli) pullImage(image string) error {
|
| ... | ... |
@@ -79,7 +80,7 @@ func newCIDFile(path string) (*cidFile, error) {
|
| 79 | 79 |
return &cidFile{path: path, file: f}, nil
|
| 80 | 80 |
} |
| 81 | 81 |
|
| 82 |
-func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
|
| 82 |
+func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
|
| 83 | 83 |
var containerIDFile *cidFile |
| 84 | 84 |
if cidfile != "" {
|
| 85 | 85 |
var err error |
| ... | ... |
@@ -107,7 +108,8 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 | 109 |
//create the container |
| 110 |
- response, err := cli.client.ContainerCreate(config, hostConfig, nil, name) |
|
| 110 |
+ response, err := cli.client.ContainerCreate(config, hostConfig, networkingConfig, name) |
|
| 111 |
+ |
|
| 111 | 112 |
//if image not found try to pull it |
| 112 | 113 |
if err != nil {
|
| 113 | 114 |
if client.IsErrImageNotFound(err) {
|
| ... | ... |
@@ -124,7 +126,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont |
| 124 | 124 |
} |
| 125 | 125 |
// Retry |
| 126 | 126 |
var retryErr error |
| 127 |
- response, retryErr = cli.client.ContainerCreate(config, hostConfig, nil, name) |
|
| 127 |
+ response, retryErr = cli.client.ContainerCreate(config, hostConfig, networkingConfig, name) |
|
| 128 | 128 |
if retryErr != nil {
|
| 129 | 129 |
return nil, retryErr |
| 130 | 130 |
} |
| ... | ... |
@@ -156,7 +158,8 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
| 156 | 156 |
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
| 157 | 157 |
) |
| 158 | 158 |
|
| 159 |
- config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 159 |
+ config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 160 |
+ |
|
| 160 | 161 |
if err != nil {
|
| 161 | 162 |
cmd.ReportError(err.Error(), true) |
| 162 | 163 |
os.Exit(1) |
| ... | ... |
@@ -165,7 +168,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
| 165 | 165 |
cmd.Usage() |
| 166 | 166 |
return nil |
| 167 | 167 |
} |
| 168 |
- response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) |
|
| 168 |
+ response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName) |
|
| 169 | 169 |
if err != nil {
|
| 170 | 170 |
return err |
| 171 | 171 |
} |
| ... | ... |
@@ -107,15 +107,22 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
|
| 107 | 107 |
|
| 108 | 108 |
// CmdNetworkConnect connects a container to a network |
| 109 | 109 |
// |
| 110 |
-// Usage: docker network connect <NETWORK> <CONTAINER> |
|
| 110 |
+// Usage: docker network connect [OPTIONS] <NETWORK> <CONTAINER> |
|
| 111 | 111 |
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
| 112 | 112 |
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
|
| 113 |
- cmd.Require(flag.Exact, 2) |
|
| 113 |
+ flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
|
|
| 114 |
+ flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
|
|
| 115 |
+ cmd.Require(flag.Min, 2) |
|
| 114 | 116 |
if err := cmd.ParseFlags(args, true); err != nil {
|
| 115 | 117 |
return err |
| 116 | 118 |
} |
| 117 |
- |
|
| 118 |
- return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), nil) |
|
| 119 |
+ epConfig := &network.EndpointSettings{
|
|
| 120 |
+ IPAMConfig: &network.EndpointIPAMConfig{
|
|
| 121 |
+ IPv4Address: *flIPAddress, |
|
| 122 |
+ IPv6Address: *flIPv6Address, |
|
| 123 |
+ }, |
|
| 124 |
+ } |
|
| 125 |
+ return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig) |
|
| 119 | 126 |
} |
| 120 | 127 |
|
| 121 | 128 |
// CmdNetworkDisconnect disconnects a container from a network |
| ... | ... |
@@ -82,7 +82,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 82 | 82 |
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
| 83 | 83 |
) |
| 84 | 84 |
|
| 85 |
- config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 85 |
+ config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args) |
|
| 86 |
+ |
|
| 86 | 87 |
// just in case the Parse does not exit |
| 87 | 88 |
if err != nil {
|
| 88 | 89 |
cmd.ReportError(err.Error(), true) |
| ... | ... |
@@ -145,7 +146,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
| 145 | 145 |
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize() |
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 |
- createResponse, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) |
|
| 148 |
+ createResponse, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName) |
|
| 149 | 149 |
if err != nil {
|
| 150 | 150 |
cmd.ReportError(err.Error(), true) |
| 151 | 151 |
return runStartContainerErr(err) |
| ... | ... |
@@ -332,7 +332,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon |
| 332 | 332 |
return err |
| 333 | 333 |
} |
| 334 | 334 |
|
| 335 |
- _, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 335 |
+ _, hostConfig, _, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 336 | 336 |
if err != nil {
|
| 337 | 337 |
return err |
| 338 | 338 |
} |
| ... | ... |
@@ -358,7 +358,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo |
| 358 | 358 |
|
| 359 | 359 |
name := r.Form.Get("name")
|
| 360 | 360 |
|
| 361 |
- config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 361 |
+ config, hostConfig, networkingConfig, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 362 | 362 |
if err != nil {
|
| 363 | 363 |
return err |
| 364 | 364 |
} |
| ... | ... |
@@ -366,10 +366,11 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo |
| 366 | 366 |
adjustCPUShares := version.LessThan("1.19")
|
| 367 | 367 |
|
| 368 | 368 |
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
| 369 |
- Name: name, |
|
| 370 |
- Config: config, |
|
| 371 |
- HostConfig: hostConfig, |
|
| 372 |
- AdjustCPUShares: adjustCPUShares, |
|
| 369 |
+ Name: name, |
|
| 370 |
+ Config: config, |
|
| 371 |
+ HostConfig: hostConfig, |
|
| 372 |
+ NetworkingConfig: networkingConfig, |
|
| 373 |
+ AdjustCPUShares: adjustCPUShares, |
|
| 373 | 374 |
}) |
| 374 | 375 |
if err != nil {
|
| 375 | 376 |
return err |
| ... | ... |
@@ -39,7 +39,7 @@ func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http. |
| 39 | 39 |
pause = true |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
- c, _, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 42 |
+ c, _, _, err := runconfig.DecodeContainerConfig(r.Body) |
|
| 43 | 43 |
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
| 44 | 44 |
return err |
| 45 | 45 |
} |
| ... | ... |
@@ -2,7 +2,6 @@ package network |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/docker/engine-api/types/network" |
| 5 |
- |
|
| 6 | 5 |
"github.com/docker/libnetwork" |
| 7 | 6 |
) |
| 8 | 7 |
|
| ... | ... |
@@ -15,7 +14,7 @@ type Backend interface {
|
| 15 | 15 |
GetAllNetworks() []libnetwork.Network |
| 16 | 16 |
CreateNetwork(name, driver string, ipam network.IPAM, |
| 17 | 17 |
options map[string]string) (libnetwork.Network, error) |
| 18 |
- ConnectContainerToNetwork(containerName, networkName string) error |
|
| 18 |
+ ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error |
|
| 19 | 19 |
DisconnectContainerFromNetwork(containerName string, |
| 20 | 20 |
network libnetwork.Network) error |
| 21 | 21 |
NetworkControllerEnabled() bool |
| ... | ... |
@@ -122,7 +122,7 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW |
| 122 | 122 |
return err |
| 123 | 123 |
} |
| 124 | 124 |
|
| 125 |
- return n.backend.ConnectContainerToNetwork(connect.Container, nw.Name()) |
|
| 125 |
+ return n.backend.ConnectContainerToNetwork(connect.Container, nw.Name(), connect.EndpointConfig) |
|
| 126 | 126 |
} |
| 127 | 127 |
|
| 128 | 128 |
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| ... | ... |
@@ -261,6 +261,14 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([] |
| 261 | 261 |
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) |
| 262 | 262 |
} |
| 263 | 263 |
|
| 264 |
+ if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
|
|
| 265 |
+ ipam := epConfig.IPAMConfig |
|
| 266 |
+ if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") {
|
|
| 267 |
+ createOptions = append(createOptions, |
|
| 268 |
+ libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil)) |
|
| 269 |
+ } |
|
| 270 |
+ } |
|
| 271 |
+ |
|
| 264 | 272 |
// Other configs are applicable only for the endpoint in the network |
| 265 | 273 |
// to which container was connected to on docker run. |
| 266 | 274 |
if n.Name() != container.HostConfig.NetworkMode.NetworkName() && |
| ... | ... |
@@ -503,7 +503,10 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li |
| 503 | 503 |
return runconfig.ErrConflictNoNetwork |
| 504 | 504 |
} |
| 505 | 505 |
} |
| 506 |
- container.NetworkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings) |
|
| 506 |
+ |
|
| 507 |
+ if _, ok := container.NetworkSettings.Networks[n.Name()]; !ok {
|
|
| 508 |
+ container.NetworkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings) |
|
| 509 |
+ } |
|
| 507 | 510 |
|
| 508 | 511 |
return nil |
| 509 | 512 |
} |
| ... | ... |
@@ -562,7 +565,12 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
| 562 | 562 |
} |
| 563 | 563 |
|
| 564 | 564 |
// updateContainerNetworkSettings update the network settings |
| 565 |
-func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container) error {
|
|
| 565 |
+func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container, endpointsConfig map[string]*networktypes.EndpointSettings) error {
|
|
| 566 |
+ var ( |
|
| 567 |
+ n libnetwork.Network |
|
| 568 |
+ err error |
|
| 569 |
+ ) |
|
| 570 |
+ |
|
| 566 | 571 |
mode := container.HostConfig.NetworkMode |
| 567 | 572 |
if container.Config.NetworkDisabled || mode.IsContainer() {
|
| 568 | 573 |
return nil |
| ... | ... |
@@ -573,14 +581,35 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai |
| 573 | 573 |
networkName = daemon.netController.Config().Daemon.DefaultNetwork |
| 574 | 574 |
} |
| 575 | 575 |
if mode.IsUserDefined() {
|
| 576 |
- n, err := daemon.FindNetwork(networkName) |
|
| 576 |
+ n, err = daemon.FindNetwork(networkName) |
|
| 577 | 577 |
if err != nil {
|
| 578 | 578 |
return err |
| 579 | 579 |
} |
| 580 | 580 |
networkName = n.Name() |
| 581 | 581 |
} |
| 582 |
- container.NetworkSettings.Networks = make(map[string]*networktypes.EndpointSettings) |
|
| 583 |
- container.NetworkSettings.Networks[networkName] = new(networktypes.EndpointSettings) |
|
| 582 |
+ if container.NetworkSettings == nil {
|
|
| 583 |
+ container.NetworkSettings = &network.Settings{}
|
|
| 584 |
+ } |
|
| 585 |
+ if endpointsConfig != nil {
|
|
| 586 |
+ container.NetworkSettings.Networks = endpointsConfig |
|
| 587 |
+ } |
|
| 588 |
+ if container.NetworkSettings.Networks == nil {
|
|
| 589 |
+ container.NetworkSettings.Networks = make(map[string]*networktypes.EndpointSettings) |
|
| 590 |
+ container.NetworkSettings.Networks[networkName] = new(networktypes.EndpointSettings) |
|
| 591 |
+ } |
|
| 592 |
+ if !mode.IsUserDefined() {
|
|
| 593 |
+ return nil |
|
| 594 |
+ } |
|
| 595 |
+ // Make sure to internally store the per network endpoint config by network name |
|
| 596 |
+ if _, ok := container.NetworkSettings.Networks[networkName]; ok {
|
|
| 597 |
+ return nil |
|
| 598 |
+ } |
|
| 599 |
+ if nwConfig, ok := container.NetworkSettings.Networks[n.ID()]; ok {
|
|
| 600 |
+ container.NetworkSettings.Networks[networkName] = nwConfig |
|
| 601 |
+ delete(container.NetworkSettings.Networks, n.ID()) |
|
| 602 |
+ return nil |
|
| 603 |
+ } |
|
| 604 |
+ |
|
| 584 | 605 |
return nil |
| 585 | 606 |
} |
| 586 | 607 |
|
| ... | ... |
@@ -598,15 +627,15 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error {
|
| 598 | 598 |
return nil |
| 599 | 599 |
} |
| 600 | 600 |
|
| 601 |
- err := daemon.updateContainerNetworkSettings(container) |
|
| 601 |
+ err := daemon.updateContainerNetworkSettings(container, nil) |
|
| 602 | 602 |
if err != nil {
|
| 603 | 603 |
return err |
| 604 | 604 |
} |
| 605 | 605 |
updateSettings = true |
| 606 | 606 |
} |
| 607 | 607 |
|
| 608 |
- for n := range container.NetworkSettings.Networks {
|
|
| 609 |
- if err := daemon.connectToNetwork(container, n, updateSettings); err != nil {
|
|
| 608 |
+ for n, nConf := range container.NetworkSettings.Networks {
|
|
| 609 |
+ if err := daemon.connectToNetwork(container, n, nConf, updateSettings); err != nil {
|
|
| 610 | 610 |
return err |
| 611 | 611 |
} |
| 612 | 612 |
} |
| ... | ... |
@@ -626,12 +655,65 @@ func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwo |
| 626 | 626 |
return sb |
| 627 | 627 |
} |
| 628 | 628 |
|
| 629 |
+// hasUserDefinedIPAddress returns whether the passed endpoint configuration contains IP address configuration |
|
| 630 |
+func hasUserDefinedIPAddress(epConfig *networktypes.EndpointSettings) bool {
|
|
| 631 |
+ return epConfig != nil && epConfig.IPAMConfig != nil && (len(epConfig.IPAMConfig.IPv4Address) > 0 || len(epConfig.IPAMConfig.IPv6Address) > 0) |
|
| 632 |
+} |
|
| 633 |
+ |
|
| 634 |
+// User specified ip address is acceptable only for networks with user specified subnets. |
|
| 635 |
+func validateNetworkingConfig(n libnetwork.Network, epConfig *networktypes.EndpointSettings) error {
|
|
| 636 |
+ if !hasUserDefinedIPAddress(epConfig) {
|
|
| 637 |
+ return nil |
|
| 638 |
+ } |
|
| 639 |
+ _, nwIPv4Configs, nwIPv6Configs := n.Info().IpamConfig() |
|
| 640 |
+ for _, s := range []struct {
|
|
| 641 |
+ ipConfigured bool |
|
| 642 |
+ subnetConfigs []*libnetwork.IpamConf |
|
| 643 |
+ }{
|
|
| 644 |
+ {
|
|
| 645 |
+ ipConfigured: len(epConfig.IPAMConfig.IPv4Address) > 0, |
|
| 646 |
+ subnetConfigs: nwIPv4Configs, |
|
| 647 |
+ }, |
|
| 648 |
+ {
|
|
| 649 |
+ ipConfigured: len(epConfig.IPAMConfig.IPv6Address) > 0, |
|
| 650 |
+ subnetConfigs: nwIPv6Configs, |
|
| 651 |
+ }, |
|
| 652 |
+ } {
|
|
| 653 |
+ if s.ipConfigured {
|
|
| 654 |
+ foundSubnet := false |
|
| 655 |
+ for _, cfg := range s.subnetConfigs {
|
|
| 656 |
+ if len(cfg.PreferredPool) > 0 {
|
|
| 657 |
+ foundSubnet = true |
|
| 658 |
+ break |
|
| 659 |
+ } |
|
| 660 |
+ } |
|
| 661 |
+ if !foundSubnet {
|
|
| 662 |
+ return runconfig.ErrUnsupportedNetworkNoSubnetAndIP |
|
| 663 |
+ } |
|
| 664 |
+ } |
|
| 665 |
+ } |
|
| 666 |
+ |
|
| 667 |
+ return nil |
|
| 668 |
+} |
|
| 669 |
+ |
|
| 670 |
+// cleanOperationalData resets the operational data from the passed endpoint settings |
|
| 671 |
+func cleanOperationalData(es *networktypes.EndpointSettings) {
|
|
| 672 |
+ es.EndpointID = "" |
|
| 673 |
+ es.Gateway = "" |
|
| 674 |
+ es.IPAddress = "" |
|
| 675 |
+ es.IPPrefixLen = 0 |
|
| 676 |
+ es.IPv6Gateway = "" |
|
| 677 |
+ es.GlobalIPv6Address = "" |
|
| 678 |
+ es.GlobalIPv6PrefixLen = 0 |
|
| 679 |
+ es.MacAddress = "" |
|
| 680 |
+} |
|
| 681 |
+ |
|
| 629 | 682 |
// ConnectToNetwork connects a container to a network |
| 630 |
-func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error {
|
|
| 683 |
+func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
|
| 631 | 684 |
if !container.Running {
|
| 632 | 685 |
return derr.ErrorCodeNotRunning.WithArgs(container.ID) |
| 633 | 686 |
} |
| 634 |
- if err := daemon.connectToNetwork(container, idOrName, true); err != nil {
|
|
| 687 |
+ if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil {
|
|
| 635 | 688 |
return err |
| 636 | 689 |
} |
| 637 | 690 |
if err := container.ToDiskLocking(); err != nil {
|
| ... | ... |
@@ -640,11 +722,15 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName |
| 640 | 640 |
return nil |
| 641 | 641 |
} |
| 642 | 642 |
|
| 643 |
-func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, updateSettings bool) (err error) {
|
|
| 643 |
+func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
|
|
| 644 | 644 |
if container.HostConfig.NetworkMode.IsContainer() {
|
| 645 | 645 |
return runconfig.ErrConflictSharedNetwork |
| 646 | 646 |
} |
| 647 | 647 |
|
| 648 |
+ if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) {
|
|
| 649 |
+ return runconfig.ErrUnsupportedNetworkAndIP |
|
| 650 |
+ } |
|
| 651 |
+ |
|
| 648 | 652 |
if containertypes.NetworkMode(idOrName).IsBridge() && |
| 649 | 653 |
daemon.configStore.DisableBridge {
|
| 650 | 654 |
container.Config.NetworkDisabled = true |
| ... | ... |
@@ -658,12 +744,20 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName |
| 658 | 658 |
return err |
| 659 | 659 |
} |
| 660 | 660 |
|
| 661 |
+ if err := validateNetworkingConfig(n, endpointConfig); err != nil {
|
|
| 662 |
+ return err |
|
| 663 |
+ } |
|
| 664 |
+ |
|
| 661 | 665 |
if updateSettings {
|
| 662 | 666 |
if err := daemon.updateNetworkSettings(container, n); err != nil {
|
| 663 | 667 |
return err |
| 664 | 668 |
} |
| 665 | 669 |
} |
| 666 | 670 |
|
| 671 |
+ if endpointConfig != nil {
|
|
| 672 |
+ container.NetworkSettings.Networks[n.Name()] = endpointConfig |
|
| 673 |
+ } |
|
| 674 |
+ |
|
| 667 | 675 |
ep, err := container.GetEndpointInNetwork(n) |
| 668 | 676 |
if err == nil {
|
| 669 | 677 |
return fmt.Errorf("Conflict. A container with name %q is already connected to network %s.", strings.TrimPrefix(container.Name, "/"), idOrName)
|
| ... | ... |
@@ -869,18 +963,16 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
|
| 869 | 869 |
|
| 870 | 870 |
sid := container.NetworkSettings.SandboxID |
| 871 | 871 |
settings := container.NetworkSettings.Networks |
| 872 |
+ if sid == "" || len(settings) == 0 {
|
|
| 873 |
+ return |
|
| 874 |
+ } |
|
| 875 |
+ |
|
| 872 | 876 |
var networks []libnetwork.Network |
| 873 |
- for n := range settings {
|
|
| 877 |
+ for n, epSettings := range settings {
|
|
| 874 | 878 |
if nw, err := daemon.FindNetwork(n); err == nil {
|
| 875 | 879 |
networks = append(networks, nw) |
| 876 | 880 |
} |
| 877 |
- settings[n] = &networktypes.EndpointSettings{}
|
|
| 878 |
- } |
|
| 879 |
- |
|
| 880 |
- container.NetworkSettings = &network.Settings{Networks: settings}
|
|
| 881 |
- |
|
| 882 |
- if sid == "" || len(settings) == 0 {
|
|
| 883 |
- return |
|
| 881 |
+ cleanOperationalData(epSettings) |
|
| 884 | 882 |
} |
| 885 | 883 |
|
| 886 | 884 |
sb, err := daemon.netController.SandboxByID(sid) |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"github.com/docker/docker/daemon/execdriver/windows" |
| 11 | 11 |
derr "github.com/docker/docker/errors" |
| 12 | 12 |
"github.com/docker/docker/layer" |
| 13 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 13 | 14 |
"github.com/docker/libnetwork" |
| 14 | 15 |
) |
| 15 | 16 |
|
| ... | ... |
@@ -18,7 +19,7 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s |
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 | 20 |
// updateContainerNetworkSettings update the network settings |
| 21 |
-func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container) error {
|
|
| 21 |
+func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container, endpointsConfig map[string]*networktypes.EndpointSettings) error {
|
|
| 22 | 22 |
return nil |
| 23 | 23 |
} |
| 24 | 24 |
|
| ... | ... |
@@ -27,7 +28,7 @@ func (daemon *Daemon) initializeNetworking(container *container.Container) error |
| 27 | 27 |
} |
| 28 | 28 |
|
| 29 | 29 |
// ConnectToNetwork connects a container to the network |
| 30 |
-func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error {
|
|
| 30 |
+func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
|
| 31 | 31 |
return nil |
| 32 | 32 |
} |
| 33 | 33 |
|
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
volumestore "github.com/docker/docker/volume/store" |
| 12 | 12 |
"github.com/docker/engine-api/types" |
| 13 | 13 |
containertypes "github.com/docker/engine-api/types/container" |
| 14 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 14 | 15 |
"github.com/opencontainers/runc/libcontainer/label" |
| 15 | 16 |
) |
| 16 | 17 |
|
| ... | ... |
@@ -108,7 +109,12 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe |
| 108 | 108 |
return nil, err |
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 |
- if err := daemon.updateContainerNetworkSettings(container); err != nil {
|
|
| 111 |
+ var endpointsConfigs map[string]*networktypes.EndpointSettings |
|
| 112 |
+ if params.NetworkingConfig != nil {
|
|
| 113 |
+ endpointsConfigs = params.NetworkingConfig.EndpointsConfig |
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ if err := daemon.updateContainerNetworkSettings(container, endpointsConfigs); err != nil {
|
|
| 112 | 117 |
return nil, err |
| 113 | 118 |
} |
| 114 | 119 |
|
| ... | ... |
@@ -150,12 +150,12 @@ func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnet |
| 150 | 150 |
// ConnectContainerToNetwork connects the given container to the given |
| 151 | 151 |
// network. If either cannot be found, an err is returned. If the |
| 152 | 152 |
// network cannot be set up, an err is returned. |
| 153 |
-func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string) error {
|
|
| 153 |
+func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error {
|
|
| 154 | 154 |
container, err := daemon.GetContainer(containerName) |
| 155 | 155 |
if err != nil {
|
| 156 | 156 |
return err |
| 157 | 157 |
} |
| 158 |
- return daemon.ConnectToNetwork(container, networkName) |
|
| 158 |
+ return daemon.ConnectToNetwork(container, networkName, endpointConfig) |
|
| 159 | 159 |
} |
| 160 | 160 |
|
| 161 | 161 |
// DisconnectContainerFromNetwork disconnects the given container from |
| ... | ... |
@@ -110,6 +110,8 @@ This section lists each version from latest to oldest. Each listing includes a |
| 110 | 110 |
* `POST /containers/create` now allows you to set a read/write rate limit for a |
| 111 | 111 |
device (in bytes per second or IO per second). |
| 112 | 112 |
* `GET /networks` now supports filtering by `name`, `id` and `type`. |
| 113 |
+* `POST /containers/create` now allows you to set the static IPv4 and/or IPv6 address for the container. |
|
| 114 |
+* `POST /networks/(id)/connect` now allows you to set the static IPv4 and/or IPv6 address for the container. |
|
| 113 | 115 |
|
| 114 | 116 |
### v1.21 API changes |
| 115 | 117 |
|
| ... | ... |
@@ -3031,7 +3031,13 @@ POST /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30/ |
| 3031 | 3031 |
Content-Type: application/json |
| 3032 | 3032 |
|
| 3033 | 3033 |
{
|
| 3034 |
- "Container":"3613f73ba0e4" |
|
| 3034 |
+ "Container":"3613f73ba0e4", |
|
| 3035 |
+ "endpoint_config": {
|
|
| 3036 |
+ "test_nw": {
|
|
| 3037 |
+ "IPv4Address":"172.24.56.89", |
|
| 3038 |
+ "IPv6Address":"2001:db8::5689" |
|
| 3039 |
+ } |
|
| 3040 |
+ } |
|
| 3035 | 3041 |
} |
| 3036 | 3042 |
``` |
| 3037 | 3043 |
|
| ... | ... |
@@ -30,11 +30,18 @@ You can also use the `docker run --net=<network-name>` option to start a contain |
| 30 | 30 |
$ docker run -itd --net=multi-host-network busybox |
| 31 | 31 |
``` |
| 32 | 32 |
|
| 33 |
+You can specify the IP address you want to be assigned to the container's interface. |
|
| 34 |
+ |
|
| 35 |
+```bash |
|
| 36 |
+$ docker network connect multi-host-network --ip 10.10.36.122 container2 |
|
| 37 |
+``` |
|
| 38 |
+ |
|
| 33 | 39 |
You can pause, restart, and stop containers that are connected to a network. |
| 34 | 40 |
Paused containers remain connected and a revealed by a `network inspect`. When |
| 35 | 41 |
the container is stopped, it does not appear on the network until you restart |
| 36 | 42 |
it. The container's IP address is not guaranteed to remain the same when a |
| 37 |
-stopped container rejoins the network. |
|
| 43 |
+stopped container rejoins the network, unless you specified one when you run |
|
| 44 |
+`docker network connect` command. |
|
| 38 | 45 |
|
| 39 | 46 |
To verify the container is connected, use the `docker network inspect` command. Use `docker network disconnect` to remove a container from the network. |
| 40 | 47 |
|
| ... | ... |
@@ -56,6 +56,8 @@ parent = "smn_cli" |
| 56 | 56 |
--log-opt=[] Log driver specific options |
| 57 | 57 |
-m, --memory="" Memory limit |
| 58 | 58 |
--mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) |
| 59 |
+ --ip="" Container IPv4 address (e.g. 172.30.100.104) |
|
| 60 |
+ --ip6="" Container IPv6 address (e.g. 2001:db8::33) |
|
| 59 | 61 |
--memory-reservation="" Memory soft limit |
| 60 | 62 |
--memory-swap="" A positive integer equal to memory plus swap. Specify -1 to enable unlimited swap. |
| 61 | 63 |
--memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. |
| ... | ... |
@@ -275,6 +275,8 @@ of the containers. |
| 275 | 275 |
'<network-name>|<network-id>': connect to a user-defined network |
| 276 | 276 |
--add-host="" : Add a line to /etc/hosts (host:IP) |
| 277 | 277 |
--mac-address="" : Sets the container's Ethernet device's MAC address |
| 278 |
+ --ip="" : Sets the container's Ethernet device's IPv4 address |
|
| 279 |
+ --ip6="" : Sets the container's Ethernet device's IPv6 address |
|
| 278 | 280 |
|
| 279 | 281 |
By default, all containers have networking enabled and they can make any |
| 280 | 282 |
outgoing connections. The operator can completely disable networking |
| ... | ... |
@@ -115,8 +115,8 @@ $ docker run -itd --name=container2 busybox |
| 115 | 115 |
Then create an isolated, `bridge` network to test with. |
| 116 | 116 |
|
| 117 | 117 |
```bash |
| 118 |
-$ docker network create -d bridge isolated_nw |
|
| 119 |
-f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a |
|
| 118 |
+$ docker network create -d bridge --subnet 172.25.0.0/16 isolated_nw |
|
| 119 |
+06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8 |
|
| 120 | 120 |
``` |
| 121 | 121 |
|
| 122 | 122 |
Connect `container2` to the network and then `inspect` the network to verify the connection: |
| ... | ... |
@@ -124,23 +124,26 @@ Connect `container2` to the network and then `inspect` the network to verify the |
| 124 | 124 |
``` |
| 125 | 125 |
$ docker network connect isolated_nw container2 |
| 126 | 126 |
$ docker network inspect isolated_nw |
| 127 |
-[[ |
|
| 127 |
+[ |
|
| 128 | 128 |
{
|
| 129 | 129 |
"Name": "isolated_nw", |
| 130 |
- "Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a", |
|
| 130 |
+ "Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8", |
|
| 131 | 131 |
"Scope": "local", |
| 132 | 132 |
"Driver": "bridge", |
| 133 | 133 |
"IPAM": {
|
| 134 | 134 |
"Driver": "default", |
| 135 | 135 |
"Config": [ |
| 136 |
- {}
|
|
| 136 |
+ {
|
|
| 137 |
+ "Subnet": "172.25.0.0/16" |
|
| 138 |
+ } |
|
| 137 | 139 |
] |
| 138 | 140 |
}, |
| 139 | 141 |
"Containers": {
|
| 140 |
- "498eaaaf328e1018042c04b2de04036fc04719a6e39a097a4f4866043a2c2152": {
|
|
| 141 |
- "EndpointID": "0e24479cfaafb029104999b4e120858a07b19b1b6d956ae56811033e45d68ad9", |
|
| 142 |
- "MacAddress": "02:42:ac:15:00:02", |
|
| 143 |
- "IPv4Address": "172.21.0.2/16", |
|
| 142 |
+ "90e1f3ec71caf82ae776a827e0712a68a110a3f175954e5bd4222fd142ac9428": {
|
|
| 143 |
+ "Name": "container2", |
|
| 144 |
+ "EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d", |
|
| 145 |
+ "MacAddress": "02:42:ac:19:00:02", |
|
| 146 |
+ "IPv4Address": "172.25.0.2/16", |
|
| 144 | 147 |
"IPv6Address": "" |
| 145 | 148 |
} |
| 146 | 149 |
}, |
| ... | ... |
@@ -150,20 +153,28 @@ $ docker network inspect isolated_nw |
| 150 | 150 |
``` |
| 151 | 151 |
|
| 152 | 152 |
You can see that the Engine automatically assigns an IP address to `container2`. |
| 153 |
-If you had specified a `--subnetwork` when creating your network, the network |
|
| 154 |
-would have used that addressing. Now, start a third container and connect it to |
|
| 153 |
+Given we specified a `--subnet` when creating the network, Engine picked |
|
| 154 |
+an address from that same subnet. Now, start a third container and connect it to |
|
| 155 | 155 |
the network on launch using the `docker run` command's `--net` option: |
| 156 | 156 |
|
| 157 | 157 |
```bash |
| 158 |
-$ docker run --net=isolated_nw -itd --name=container3 busybox |
|
| 159 |
-c282ca437ee7e926a7303a64fc04109740208d2c20e442366139322211a6481c |
|
| 158 |
+$ docker run --net=isolated_nw --ip=172.25.3.3 -itd --name=container3 busybox |
|
| 159 |
+467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551 |
|
| 160 | 160 |
``` |
| 161 | 161 |
|
| 162 |
+As you can see you were able to specify the ip address for your container. |
|
| 163 |
+As long as the network to which the container is connecting was created with |
|
| 164 |
+a user specified subnet, you will be able to select the IPv4 and/or IPv6 address(es) |
|
| 165 |
+for your container when executing `docker run` and `docker network connect` commands. |
|
| 166 |
+The selected IP address is part of the container networking configuration and will be |
|
| 167 |
+preserved across container reload. The feature is only available on user defined networks, |
|
| 168 |
+because they guarantee their subnets configuration does not change across daemon reload. |
|
| 169 |
+ |
|
| 162 | 170 |
Now, inspect the network resources used by `container3`. |
| 163 | 171 |
|
| 164 | 172 |
```bash |
| 165 | 173 |
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container3
|
| 166 |
-{"isolated_nw":{"EndpointID":"e5d077f9712a69c6929fdd890df5e7c1c649771a50df5b422f7e68f0ae61e847","Gateway":"172.21.0.1","IPAddress":"172.21.0.3","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:15:00:03"}}
|
|
| 174 |
+{"isolated_nw":{"IPAMConfig":{"IPv4Address":"172.25.3.3"},"EndpointID":"dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103","Gateway":"172.25.0.1","IPAddress":"172.25.3.3","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:19:03:03"}}
|
|
| 167 | 175 |
``` |
| 168 | 176 |
Repeat this command for `container2`. If you have Python installed, you can pretty print the output. |
| 169 | 177 |
|
| ... | ... |
@@ -171,24 +182,26 @@ Repeat this command for `container2`. If you have Python installed, you can pret |
| 171 | 171 |
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | python -m json.tool
|
| 172 | 172 |
{
|
| 173 | 173 |
"bridge": {
|
| 174 |
- "EndpointID": "281b5ead415cf48a6a84fd1a6504342c76e9091fe09b4fdbcc4a01c30b0d3c5b", |
|
| 174 |
+ "EndpointID": "0099f9efb5a3727f6a554f176b1e96fca34cae773da68b3b6a26d046c12cb365", |
|
| 175 | 175 |
"Gateway": "172.17.0.1", |
| 176 | 176 |
"GlobalIPv6Address": "", |
| 177 | 177 |
"GlobalIPv6PrefixLen": 0, |
| 178 |
+ "IPAMConfig": null, |
|
| 178 | 179 |
"IPAddress": "172.17.0.3", |
| 179 | 180 |
"IPPrefixLen": 16, |
| 180 | 181 |
"IPv6Gateway": "", |
| 181 | 182 |
"MacAddress": "02:42:ac:11:00:03" |
| 182 | 183 |
}, |
| 183 | 184 |
"isolated_nw": {
|
| 184 |
- "EndpointID": "0e24479cfaafb029104999b4e120858a07b19b1b6d956ae56811033e45d68ad9", |
|
| 185 |
- "Gateway": "172.21.0.1", |
|
| 185 |
+ "EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d", |
|
| 186 |
+ "Gateway": "172.25.0.1", |
|
| 186 | 187 |
"GlobalIPv6Address": "", |
| 187 | 188 |
"GlobalIPv6PrefixLen": 0, |
| 188 |
- "IPAddress": "172.21.0.2", |
|
| 189 |
+ "IPAMConfig": null, |
|
| 190 |
+ "IPAddress": "172.25.0.2", |
|
| 189 | 191 |
"IPPrefixLen": 16, |
| 190 | 192 |
"IPv6Gateway": "", |
| 191 |
- "MacAddress": "02:42:ac:15:00:02" |
|
| 193 |
+ "MacAddress": "02:42:ac:19:00:02" |
|
| 192 | 194 |
} |
| 193 | 195 |
} |
| 194 | 196 |
``` |
| ... | ... |
@@ -223,8 +236,8 @@ eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03 |
| 223 | 223 |
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B) |
| 224 | 224 |
|
| 225 | 225 |
eth1 Link encap:Ethernet HWaddr 02:42:AC:15:00:02 |
| 226 |
- inet addr:172.21.0.2 Bcast:0.0.0.0 Mask:255.255.0.0 |
|
| 227 |
- inet6 addr: fe80::42:acff:fe15:2/64 Scope:Link |
|
| 226 |
+ inet addr:172.25.0.2 Bcast:0.0.0.0 Mask:255.255.0.0 |
|
| 227 |
+ inet6 addr: fe80::42:acff:fe19:2/64 Scope:Link |
|
| 228 | 228 |
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 |
| 229 | 229 |
RX packets:8 errors:0 dropped:0 overruns:0 frame:0 |
| 230 | 230 |
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 |
| ... | ... |
@@ -252,19 +265,19 @@ fe00::0 ip6-localnet |
| 252 | 252 |
ff00::0 ip6-mcastprefix |
| 253 | 253 |
ff02::1 ip6-allnodes |
| 254 | 254 |
ff02::2 ip6-allrouters |
| 255 |
-172.21.0.3 container3 |
|
| 256 |
-172.21.0.3 container3.isolated_nw |
|
| 255 |
+172.21.3.3 container3 |
|
| 256 |
+172.21.3.3 container3.isolated_nw |
|
| 257 | 257 |
``` |
| 258 | 258 |
|
| 259 | 259 |
On the `isolated_nw` which was user defined, the Docker network feature updated the `/etc/hosts` with the proper name resolution. Inside of `container2` it is possible to ping `container3` by name. |
| 260 | 260 |
|
| 261 | 261 |
```bash |
| 262 | 262 |
/ # ping -w 4 container3 |
| 263 |
-PING container3 (172.21.0.3): 56 data bytes |
|
| 264 |
-64 bytes from 172.21.0.3: seq=0 ttl=64 time=0.070 ms |
|
| 265 |
-64 bytes from 172.21.0.3: seq=1 ttl=64 time=0.080 ms |
|
| 266 |
-64 bytes from 172.21.0.3: seq=2 ttl=64 time=0.080 ms |
|
| 267 |
-64 bytes from 172.21.0.3: seq=3 ttl=64 time=0.097 ms |
|
| 263 |
+PING container3 (172.25.3.3): 56 data bytes |
|
| 264 |
+64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.070 ms |
|
| 265 |
+64 bytes from 172.25.3.3: seq=1 ttl=64 time=0.080 ms |
|
| 266 |
+64 bytes from 172.25.3.3: seq=2 ttl=64 time=0.080 ms |
|
| 267 |
+64 bytes from 172.25.3.3: seq=3 ttl=64 time=0.097 ms |
|
| 268 | 268 |
|
| 269 | 269 |
--- container3 ping statistics --- |
| 270 | 270 |
4 packets transmitted, 4 packets received, 0% packet loss |
| ... | ... |
@@ -342,23 +355,26 @@ docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | pytho
|
| 342 | 342 |
|
| 343 | 343 |
|
| 344 | 344 |
$ docker network inspect isolated_nw |
| 345 |
-[[ |
|
| 345 |
+[ |
|
| 346 | 346 |
{
|
| 347 | 347 |
"Name": "isolated_nw", |
| 348 |
- "Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a", |
|
| 348 |
+ "Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8", |
|
| 349 | 349 |
"Scope": "local", |
| 350 | 350 |
"Driver": "bridge", |
| 351 | 351 |
"IPAM": {
|
| 352 | 352 |
"Driver": "default", |
| 353 | 353 |
"Config": [ |
| 354 |
- {}
|
|
| 354 |
+ {
|
|
| 355 |
+ "Subnet": "172.25.0.0/16" |
|
| 356 |
+ } |
|
| 355 | 357 |
] |
| 356 | 358 |
}, |
| 357 | 359 |
"Containers": {
|
| 358 |
- "c282ca437ee7e926a7303a64fc04109740208d2c20e442366139322211a6481c": {
|
|
| 359 |
- "EndpointID": "e5d077f9712a69c6929fdd890df5e7c1c649771a50df5b422f7e68f0ae61e847", |
|
| 360 |
- "MacAddress": "02:42:ac:15:00:03", |
|
| 361 |
- "IPv4Address": "172.21.0.3/16", |
|
| 360 |
+ "467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551": {
|
|
| 361 |
+ "Name": "container3", |
|
| 362 |
+ "EndpointID": "dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103", |
|
| 363 |
+ "MacAddress": "02:42:ac:19:03:03", |
|
| 364 |
+ "IPv4Address": "172.25.3.3/16", |
|
| 362 | 365 |
"IPv6Address": "" |
| 363 | 366 |
} |
| 364 | 367 |
}, |
| ... | ... |
@@ -393,7 +409,7 @@ lo Link encap:Local Loopback |
| 393 | 393 |
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) |
| 394 | 394 |
|
| 395 | 395 |
/ # ping container3 |
| 396 |
-PING container3 (172.20.0.1): 56 data bytes |
|
| 396 |
+PING container3 (172.25.3.3): 56 data bytes |
|
| 397 | 397 |
^C |
| 398 | 398 |
--- container3 ping statistics --- |
| 399 | 399 |
2 packets transmitted, 0 packets received, 100% packet loss |
| ... | ... |
@@ -426,13 +442,15 @@ docker network inspect isolated_nw |
| 426 | 426 |
[ |
| 427 | 427 |
{
|
| 428 | 428 |
"Name": "isolated_nw", |
| 429 |
- "Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a", |
|
| 429 |
+ "Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8", |
|
| 430 | 430 |
"Scope": "local", |
| 431 | 431 |
"Driver": "bridge", |
| 432 | 432 |
"IPAM": {
|
| 433 | 433 |
"Driver": "default", |
| 434 | 434 |
"Config": [ |
| 435 |
- {}
|
|
| 435 |
+ {
|
|
| 436 |
+ "Subnet": "172.25.0.0/16" |
|
| 437 |
+ } |
|
| 436 | 438 |
] |
| 437 | 439 |
}, |
| 438 | 440 |
"Containers": {},
|
| ... | ... |
@@ -962,3 +962,71 @@ func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMulipleNetworks(c *chec |
| 962 | 962 |
c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network"))
|
| 963 | 963 |
c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' netwokr"))
|
| 964 | 964 |
} |
| 965 |
+ |
|
| 966 |
+func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
|
|
| 967 |
+ // create two networks |
|
| 968 |
+ dockerCmd(c, "network", "create", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0") |
|
| 969 |
+ assertNwIsAvailable(c, "n0") |
|
| 970 |
+ |
|
| 971 |
+ dockerCmd(c, "network", "create", "--subnet=172.30.0.0/16", "--ip-range=172.30.5.0/24", "--subnet=2001:db8:abcd::/64", "--ip-range=2001:db8:abcd::/80", "n1") |
|
| 972 |
+ assertNwIsAvailable(c, "n1") |
|
| 973 |
+ |
|
| 974 |
+ // run a container on first network specifying the ip addresses |
|
| 975 |
+ dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top") |
|
| 976 |
+ c.Assert(waitRun("c0"), check.IsNil)
|
|
| 977 |
+ verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") |
|
| 978 |
+ |
|
| 979 |
+ // connect the container to the second network specifying the preferred ip addresses |
|
| 980 |
+ dockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n1", "c0") |
|
| 981 |
+ verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544") |
|
| 982 |
+ |
|
| 983 |
+ // Stop and restart the container |
|
| 984 |
+ dockerCmd(c, "stop", "c0") |
|
| 985 |
+ dockerCmd(c, "start", "c0") |
|
| 986 |
+ |
|
| 987 |
+ // verify preferred addresses are applied |
|
| 988 |
+ verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988") |
|
| 989 |
+ verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544") |
|
| 990 |
+ |
|
| 991 |
+ // Still it should fail to connect to the default network with a specified IP (whatever ip) |
|
| 992 |
+ out, _, err := dockerCmdWithError("network", "connect", "--ip", "172.21.55.44", "bridge", "c0")
|
|
| 993 |
+ c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
|
| 994 |
+ c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndIP.Error()) |
|
| 995 |
+ |
|
| 996 |
+} |
|
| 997 |
+ |
|
| 998 |
+func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedPreferredIP(c *check.C) {
|
|
| 999 |
+ // preferred IP is not supported on predefined networks |
|
| 1000 |
+ for _, mode := range []string{"none", "host", "bridge"} {
|
|
| 1001 |
+ checkUnsupportedNetworkAndIP(c, mode) |
|
| 1002 |
+ } |
|
| 1003 |
+ |
|
| 1004 |
+ // preferred IP is not supported on networks with no user defined subnets |
|
| 1005 |
+ dockerCmd(c, "network", "create", "n0") |
|
| 1006 |
+ assertNwIsAvailable(c, "n0") |
|
| 1007 |
+ |
|
| 1008 |
+ out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top")
|
|
| 1009 |
+ c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
|
| 1010 |
+ c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error()) |
|
| 1011 |
+ |
|
| 1012 |
+ out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top")
|
|
| 1013 |
+ c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
|
| 1014 |
+ c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error()) |
|
| 1015 |
+ |
|
| 1016 |
+ dockerCmd(c, "network", "rm", "n0") |
|
| 1017 |
+ assertNwNotAvailable(c, "n0") |
|
| 1018 |
+} |
|
| 1019 |
+ |
|
| 1020 |
+func checkUnsupportedNetworkAndIP(c *check.C, nwMode string) {
|
|
| 1021 |
+ out, _, err := dockerCmdWithError("run", "-d", "--net", nwMode, "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
|
|
| 1022 |
+ c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
|
| 1023 |
+ c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndIP.Error()) |
|
| 1024 |
+} |
|
| 1025 |
+ |
|
| 1026 |
+func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
|
|
| 1027 |
+ out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", nwname), cName)
|
|
| 1028 |
+ c.Assert(strings.TrimSpace(out), check.Equals, ipv4) |
|
| 1029 |
+ |
|
| 1030 |
+ out, _ = dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.GlobalIPv6Address }}'", nwname), cName)
|
|
| 1031 |
+ c.Assert(strings.TrimSpace(out), check.Equals, ipv6) |
|
| 1032 |
+} |
| ... | ... |
@@ -7,18 +7,19 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/docker/docker/volume" |
| 9 | 9 |
"github.com/docker/engine-api/types/container" |
| 10 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 10 | 11 |
) |
| 11 | 12 |
|
| 12 | 13 |
// DecodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper |
| 13 | 14 |
// struct and returns both a Config and an HostConfig struct |
| 14 | 15 |
// Be aware this function is not checking whether the resulted structs are nil, |
| 15 | 16 |
// it's your business to do so |
| 16 |
-func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostConfig, error) {
|
|
| 17 |
+func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
|
| 17 | 18 |
var w ContainerConfigWrapper |
| 18 | 19 |
|
| 19 | 20 |
decoder := json.NewDecoder(src) |
| 20 | 21 |
if err := decoder.Decode(&w); err != nil {
|
| 21 |
- return nil, nil, err |
|
| 22 |
+ return nil, nil, nil, err |
|
| 22 | 23 |
} |
| 23 | 24 |
|
| 24 | 25 |
hc := w.getHostConfig() |
| ... | ... |
@@ -33,21 +34,21 @@ func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostCon |
| 33 | 33 |
|
| 34 | 34 |
// Now validate all the volumes and binds |
| 35 | 35 |
if err := validateVolumesAndBindSettings(w.Config, hc); err != nil {
|
| 36 |
- return nil, nil, err |
|
| 36 |
+ return nil, nil, nil, err |
|
| 37 | 37 |
} |
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 | 40 |
// Certain parameters need daemon-side validation that cannot be done |
| 41 | 41 |
// on the client, as only the daemon knows what is valid for the platform. |
| 42 | 42 |
if err := ValidateNetMode(w.Config, hc); err != nil {
|
| 43 |
- return nil, nil, err |
|
| 43 |
+ return nil, nil, nil, err |
|
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 | 46 |
// Validate the isolation level |
| 47 | 47 |
if err := ValidateIsolationLevel(hc); err != nil {
|
| 48 |
- return nil, nil, err |
|
| 48 |
+ return nil, nil, nil, err |
|
| 49 | 49 |
} |
| 50 |
- return w.Config, hc, nil |
|
| 50 |
+ return w.Config, hc, w.NetworkingConfig, nil |
|
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 | 53 |
// validateVolumesAndBindSettings validates each of the volumes and bind settings |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
"testing" |
| 11 | 11 |
|
| 12 | 12 |
"github.com/docker/engine-api/types/container" |
| 13 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 13 | 14 |
"github.com/docker/engine-api/types/strslice" |
| 14 | 15 |
) |
| 15 | 16 |
|
| ... | ... |
@@ -45,7 +46,7 @@ func TestDecodeContainerConfig(t *testing.T) {
|
| 45 | 45 |
t.Fatal(err) |
| 46 | 46 |
} |
| 47 | 47 |
|
| 48 |
- c, h, err := DecodeContainerConfig(bytes.NewReader(b)) |
|
| 48 |
+ c, h, _, err := DecodeContainerConfig(bytes.NewReader(b)) |
|
| 49 | 49 |
if err != nil {
|
| 50 | 50 |
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
| 51 | 51 |
} |
| ... | ... |
@@ -70,29 +71,29 @@ func TestDecodeContainerConfig(t *testing.T) {
|
| 70 | 70 |
func TestDecodeContainerConfigIsolation(t *testing.T) {
|
| 71 | 71 |
|
| 72 | 72 |
// An invalid isolation level |
| 73 |
- if _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
|
| 73 |
+ if _, _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
|
| 74 | 74 |
if !strings.Contains(err.Error(), `invalid --isolation: "invalid"`) {
|
| 75 | 75 |
t.Fatal(err) |
| 76 | 76 |
} |
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 | 79 |
// Blank isolation level (== default) |
| 80 |
- if _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
|
| 80 |
+ if _, _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
|
| 81 | 81 |
t.Fatal("Blank isolation should have succeeded")
|
| 82 | 82 |
} |
| 83 | 83 |
|
| 84 | 84 |
// Default isolation level |
| 85 |
- if _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
|
| 85 |
+ if _, _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
|
| 86 | 86 |
t.Fatal("default isolation should have succeeded")
|
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 | 89 |
// Hyper-V Containers isolation level (Valid on Windows only) |
| 90 | 90 |
if runtime.GOOS == "windows" {
|
| 91 |
- if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
|
| 91 |
+ if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
|
| 92 | 92 |
t.Fatal("hyperv isolation should have succeeded")
|
| 93 | 93 |
} |
| 94 | 94 |
} else {
|
| 95 |
- if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
|
| 95 |
+ if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
|
| 96 | 96 |
if !strings.Contains(err.Error(), `invalid --isolation: "hyperv"`) {
|
| 97 | 97 |
t.Fatal(err) |
| 98 | 98 |
} |
| ... | ... |
@@ -102,7 +103,7 @@ func TestDecodeContainerConfigIsolation(t *testing.T) {
|
| 102 | 102 |
|
| 103 | 103 |
// callDecodeContainerConfigIsolation is a utility function to call |
| 104 | 104 |
// DecodeContainerConfig for validating isolation levels |
| 105 |
-func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *container.HostConfig, error) {
|
|
| 105 |
+func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
|
| 106 | 106 |
var ( |
| 107 | 107 |
b []byte |
| 108 | 108 |
err error |
| ... | ... |
@@ -114,7 +115,7 @@ func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *c |
| 114 | 114 |
Isolation: container.IsolationLevel(isolation)}, |
| 115 | 115 |
} |
| 116 | 116 |
if b, err = json.Marshal(w); err != nil {
|
| 117 |
- return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
|
| 117 |
+ return nil, nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
|
| 118 | 118 |
} |
| 119 | 119 |
return DecodeContainerConfig(bytes.NewReader(b)) |
| 120 | 120 |
} |
| ... | ... |
@@ -2,15 +2,19 @@ |
| 2 | 2 |
|
| 3 | 3 |
package runconfig |
| 4 | 4 |
|
| 5 |
-import "github.com/docker/engine-api/types/container" |
|
| 5 |
+import ( |
|
| 6 |
+ "github.com/docker/engine-api/types/container" |
|
| 7 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 8 |
+) |
|
| 6 | 9 |
|
| 7 | 10 |
// ContainerConfigWrapper is a Config wrapper that hold the container Config (portable) |
| 8 | 11 |
// and the corresponding HostConfig (non-portable). |
| 9 | 12 |
type ContainerConfigWrapper struct {
|
| 10 | 13 |
*container.Config |
| 11 |
- InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"` |
|
| 12 |
- Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility. |
|
| 13 |
- *container.HostConfig // Deprecated. Exported to read attributes from json that are not in the inner host config structure. |
|
| 14 |
+ InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"` |
|
| 15 |
+ Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility. |
|
| 16 |
+ NetworkingConfig *networktypes.NetworkingConfig `json:"NetworkingConfig,omitempty"` |
|
| 17 |
+ *container.HostConfig // Deprecated. Exported to read attributes from json that are not in the inner host config structure. |
|
| 14 | 18 |
} |
| 15 | 19 |
|
| 16 | 20 |
// getHostConfig gets the HostConfig of the Config. |
| ... | ... |
@@ -1,12 +1,16 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 |
-import "github.com/docker/engine-api/types/container" |
|
| 3 |
+import ( |
|
| 4 |
+ "github.com/docker/engine-api/types/container" |
|
| 5 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 6 |
+) |
|
| 4 | 7 |
|
| 5 | 8 |
// ContainerConfigWrapper is a Config wrapper that hold the container Config (portable) |
| 6 | 9 |
// and the corresponding HostConfig (non-portable). |
| 7 | 10 |
type ContainerConfigWrapper struct {
|
| 8 | 11 |
*container.Config |
| 9 |
- HostConfig *container.HostConfig `json:"HostConfig,omitempty"` |
|
| 12 |
+ HostConfig *container.HostConfig `json:"HostConfig,omitempty"` |
|
| 13 |
+ NetworkingConfig *networktypes.NetworkingConfig `json:"NetworkingConfig,omitempty"` |
|
| 10 | 14 |
} |
| 11 | 15 |
|
| 12 | 16 |
// getHostConfig gets the HostConfig of the Config. |
| ... | ... |
@@ -29,4 +29,8 @@ var ( |
| 29 | 29 |
ErrConflictNetworkPublishPorts = fmt.Errorf("Conflicting options: port publishing and the container type network mode")
|
| 30 | 30 |
// ErrConflictNetworkExposePorts conflict between the expose option and the network mode |
| 31 | 31 |
ErrConflictNetworkExposePorts = fmt.Errorf("Conflicting options: port exposing and the container type network mode")
|
| 32 |
+ // ErrUnsupportedNetworkAndIP conflict between network mode and preferred ip address |
|
| 33 |
+ ErrUnsupportedNetworkAndIP = fmt.Errorf("User specified IP address is supported on user defined networks only")
|
|
| 34 |
+ // ErrUnsupportedNetworkNoSubnetAndIP conflict between network with no configured subnet and preferred ip address |
|
| 35 |
+ ErrUnsupportedNetworkNoSubnetAndIP = fmt.Errorf("User specified IP address is supported only when connecting to networks with user configured subnets")
|
|
| 32 | 36 |
) |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/pkg/mount" |
| 12 | 12 |
"github.com/docker/docker/pkg/signal" |
| 13 | 13 |
"github.com/docker/engine-api/types/container" |
| 14 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 14 | 15 |
"github.com/docker/engine-api/types/strslice" |
| 15 | 16 |
"github.com/docker/go-connections/nat" |
| 16 | 17 |
"github.com/docker/go-units" |
| ... | ... |
@@ -19,7 +20,7 @@ import ( |
| 19 | 19 |
// Parse parses the specified args for the specified command and generates a Config, |
| 20 | 20 |
// a HostConfig and returns them with the specified command. |
| 21 | 21 |
// If the specified args are not valid, it will return an error. |
| 22 |
-func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *flag.FlagSet, error) {
|
|
| 22 |
+func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
|
| 23 | 23 |
var ( |
| 24 | 24 |
// FIXME: use utils.ListOpts for attach and volumes? |
| 25 | 25 |
flAttach = opts.NewListOpts(ValidateAttach) |
| ... | ... |
@@ -77,6 +78,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 77 | 77 |
flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)")
|
| 78 | 78 |
flNetMode = cmd.String([]string{"-net"}, "default", "Connect a container to a network")
|
| 79 | 79 |
flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
|
| 80 |
+ flIPv4Address = cmd.String([]string{"-ip"}, "", "Container IPv4 address (e.g. 172.30.100.104)")
|
|
| 81 |
+ flIPv6Address = cmd.String([]string{"-ip6"}, "", "Container IPv6 address (e.g. 2001:db8::33)")
|
|
| 80 | 82 |
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
| 81 | 83 |
flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
| 82 | 84 |
flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
|
| ... | ... |
@@ -119,7 +122,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 119 | 119 |
cmd.Require(flag.Min, 1) |
| 120 | 120 |
|
| 121 | 121 |
if err := cmd.ParseFlags(args, true); err != nil {
|
| 122 |
- return nil, nil, cmd, err |
|
| 122 |
+ return nil, nil, nil, cmd, err |
|
| 123 | 123 |
} |
| 124 | 124 |
|
| 125 | 125 |
var ( |
| ... | ... |
@@ -131,7 +134,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 131 | 131 |
// Validate the input mac address |
| 132 | 132 |
if *flMacAddress != "" {
|
| 133 | 133 |
if _, err := ValidateMACAddress(*flMacAddress); err != nil {
|
| 134 |
- return nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
|
| 134 |
+ return nil, nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
|
| 135 | 135 |
} |
| 136 | 136 |
} |
| 137 | 137 |
if *flStdin {
|
| ... | ... |
@@ -149,7 +152,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 149 | 149 |
if *flMemoryString != "" {
|
| 150 | 150 |
flMemory, err = units.RAMInBytes(*flMemoryString) |
| 151 | 151 |
if err != nil {
|
| 152 |
- return nil, nil, cmd, err |
|
| 152 |
+ return nil, nil, nil, cmd, err |
|
| 153 | 153 |
} |
| 154 | 154 |
} |
| 155 | 155 |
|
| ... | ... |
@@ -157,7 +160,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 157 | 157 |
if *flMemoryReservation != "" {
|
| 158 | 158 |
MemoryReservation, err = units.RAMInBytes(*flMemoryReservation) |
| 159 | 159 |
if err != nil {
|
| 160 |
- return nil, nil, cmd, err |
|
| 160 |
+ return nil, nil, nil, cmd, err |
|
| 161 | 161 |
} |
| 162 | 162 |
} |
| 163 | 163 |
|
| ... | ... |
@@ -168,7 +171,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 168 | 168 |
} else {
|
| 169 | 169 |
memorySwap, err = units.RAMInBytes(*flMemorySwap) |
| 170 | 170 |
if err != nil {
|
| 171 |
- return nil, nil, cmd, err |
|
| 171 |
+ return nil, nil, nil, cmd, err |
|
| 172 | 172 |
} |
| 173 | 173 |
} |
| 174 | 174 |
} |
| ... | ... |
@@ -177,20 +180,20 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 177 | 177 |
if *flKernelMemory != "" {
|
| 178 | 178 |
KernelMemory, err = units.RAMInBytes(*flKernelMemory) |
| 179 | 179 |
if err != nil {
|
| 180 |
- return nil, nil, cmd, err |
|
| 180 |
+ return nil, nil, nil, cmd, err |
|
| 181 | 181 |
} |
| 182 | 182 |
} |
| 183 | 183 |
|
| 184 | 184 |
swappiness := *flSwappiness |
| 185 | 185 |
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
| 186 |
- return nil, nil, cmd, fmt.Errorf("Invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
|
| 186 |
+ return nil, nil, nil, cmd, fmt.Errorf("Invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
|
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 | 189 |
var shmSize int64 |
| 190 | 190 |
if *flShmSize != "" {
|
| 191 | 191 |
shmSize, err = units.RAMInBytes(*flShmSize) |
| 192 | 192 |
if err != nil {
|
| 193 |
- return nil, nil, cmd, err |
|
| 193 |
+ return nil, nil, nil, cmd, err |
|
| 194 | 194 |
} |
| 195 | 195 |
} |
| 196 | 196 |
|
| ... | ... |
@@ -210,7 +213,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 210 | 210 |
for _, t := range flTmpfs.GetAll() {
|
| 211 | 211 |
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
| 212 | 212 |
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
| 213 |
- return nil, nil, cmd, err |
|
| 213 |
+ return nil, nil, nil, cmd, err |
|
| 214 | 214 |
} |
| 215 | 215 |
tmpfs[arr[0]] = arr[1] |
| 216 | 216 |
} else {
|
| ... | ... |
@@ -243,13 +246,13 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 243 | 243 |
|
| 244 | 244 |
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll()) |
| 245 | 245 |
if err != nil {
|
| 246 |
- return nil, nil, cmd, err |
|
| 246 |
+ return nil, nil, nil, cmd, err |
|
| 247 | 247 |
} |
| 248 | 248 |
|
| 249 | 249 |
// Merge in exposed ports to the map of published ports |
| 250 | 250 |
for _, e := range flExpose.GetAll() {
|
| 251 | 251 |
if strings.Contains(e, ":") {
|
| 252 |
- return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
|
|
| 252 |
+ return nil, nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
|
|
| 253 | 253 |
} |
| 254 | 254 |
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] |
| 255 | 255 |
proto, port := nat.SplitProtoPort(e) |
| ... | ... |
@@ -257,12 +260,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 257 | 257 |
//if expose a port, the start and end port are the same |
| 258 | 258 |
start, end, err := nat.ParsePortRange(port) |
| 259 | 259 |
if err != nil {
|
| 260 |
- return nil, nil, cmd, fmt.Errorf("Invalid range format for --expose: %s, error: %s", e, err)
|
|
| 260 |
+ return nil, nil, nil, cmd, fmt.Errorf("Invalid range format for --expose: %s, error: %s", e, err)
|
|
| 261 | 261 |
} |
| 262 | 262 |
for i := start; i <= end; i++ {
|
| 263 | 263 |
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10)) |
| 264 | 264 |
if err != nil {
|
| 265 |
- return nil, nil, cmd, err |
|
| 265 |
+ return nil, nil, nil, cmd, err |
|
| 266 | 266 |
} |
| 267 | 267 |
if _, exists := ports[p]; !exists {
|
| 268 | 268 |
ports[p] = struct{}{}
|
| ... | ... |
@@ -275,7 +278,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 275 | 275 |
for _, device := range flDevices.GetAll() {
|
| 276 | 276 |
deviceMapping, err := ParseDevice(device) |
| 277 | 277 |
if err != nil {
|
| 278 |
- return nil, nil, cmd, err |
|
| 278 |
+ return nil, nil, nil, cmd, err |
|
| 279 | 279 |
} |
| 280 | 280 |
deviceMappings = append(deviceMappings, deviceMapping) |
| 281 | 281 |
} |
| ... | ... |
@@ -283,38 +286,38 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 283 | 283 |
// collect all the environment variables for the container |
| 284 | 284 |
envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll()) |
| 285 | 285 |
if err != nil {
|
| 286 |
- return nil, nil, cmd, err |
|
| 286 |
+ return nil, nil, nil, cmd, err |
|
| 287 | 287 |
} |
| 288 | 288 |
|
| 289 | 289 |
// collect all the labels for the container |
| 290 | 290 |
labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll()) |
| 291 | 291 |
if err != nil {
|
| 292 |
- return nil, nil, cmd, err |
|
| 292 |
+ return nil, nil, nil, cmd, err |
|
| 293 | 293 |
} |
| 294 | 294 |
|
| 295 | 295 |
ipcMode := container.IpcMode(*flIpcMode) |
| 296 | 296 |
if !ipcMode.Valid() {
|
| 297 |
- return nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
|
|
| 297 |
+ return nil, nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
|
|
| 298 | 298 |
} |
| 299 | 299 |
|
| 300 | 300 |
pidMode := container.PidMode(*flPidMode) |
| 301 | 301 |
if !pidMode.Valid() {
|
| 302 |
- return nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
|
|
| 302 |
+ return nil, nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
|
|
| 303 | 303 |
} |
| 304 | 304 |
|
| 305 | 305 |
utsMode := container.UTSMode(*flUTSMode) |
| 306 | 306 |
if !utsMode.Valid() {
|
| 307 |
- return nil, nil, cmd, fmt.Errorf("--uts: invalid UTS mode")
|
|
| 307 |
+ return nil, nil, nil, cmd, fmt.Errorf("--uts: invalid UTS mode")
|
|
| 308 | 308 |
} |
| 309 | 309 |
|
| 310 | 310 |
restartPolicy, err := ParseRestartPolicy(*flRestartPolicy) |
| 311 | 311 |
if err != nil {
|
| 312 |
- return nil, nil, cmd, err |
|
| 312 |
+ return nil, nil, nil, cmd, err |
|
| 313 | 313 |
} |
| 314 | 314 |
|
| 315 | 315 |
loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll()) |
| 316 | 316 |
if err != nil {
|
| 317 |
- return nil, nil, cmd, err |
|
| 317 |
+ return nil, nil, nil, cmd, err |
|
| 318 | 318 |
} |
| 319 | 319 |
|
| 320 | 320 |
resources := container.Resources{
|
| ... | ... |
@@ -405,7 +408,21 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host |
| 405 | 405 |
if config.OpenStdin && config.AttachStdin {
|
| 406 | 406 |
config.StdinOnce = true |
| 407 | 407 |
} |
| 408 |
- return config, hostConfig, cmd, nil |
|
| 408 |
+ |
|
| 409 |
+ var networkingConfig *networktypes.NetworkingConfig |
|
| 410 |
+ if *flIPv4Address != "" || *flIPv6Address != "" {
|
|
| 411 |
+ networkingConfig = &networktypes.NetworkingConfig{
|
|
| 412 |
+ EndpointsConfig: make(map[string]*networktypes.EndpointSettings), |
|
| 413 |
+ } |
|
| 414 |
+ networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
|
| 415 |
+ IPAMConfig: &networktypes.EndpointIPAMConfig{
|
|
| 416 |
+ IPv4Address: *flIPv4Address, |
|
| 417 |
+ IPv6Address: *flIPv6Address, |
|
| 418 |
+ }, |
|
| 419 |
+ } |
|
| 420 |
+ } |
|
| 421 |
+ |
|
| 422 |
+ return config, hostConfig, networkingConfig, cmd, nil |
|
| 409 | 423 |
} |
| 410 | 424 |
|
| 411 | 425 |
// reads a file of line terminated key=value pairs and override that with override parameter |
| ... | ... |
@@ -13,10 +13,11 @@ import ( |
| 13 | 13 |
flag "github.com/docker/docker/pkg/mflag" |
| 14 | 14 |
"github.com/docker/docker/runconfig" |
| 15 | 15 |
"github.com/docker/engine-api/types/container" |
| 16 |
+ networktypes "github.com/docker/engine-api/types/network" |
|
| 16 | 17 |
"github.com/docker/go-connections/nat" |
| 17 | 18 |
) |
| 18 | 19 |
|
| 19 |
-func parseRun(args []string) (*container.Config, *container.HostConfig, *flag.FlagSet, error) {
|
|
| 20 |
+func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
|
| 20 | 21 |
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
| 21 | 22 |
cmd.SetOutput(ioutil.Discard) |
| 22 | 23 |
cmd.Usage = nil |
| ... | ... |
@@ -24,7 +25,7 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *flag.Fl |
| 24 | 24 |
} |
| 25 | 25 |
|
| 26 | 26 |
func parse(t *testing.T, args string) (*container.Config, *container.HostConfig, error) {
|
| 27 |
- config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) |
|
| 27 |
+ config, hostConfig, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) |
|
| 28 | 28 |
return config, hostConfig, err |
| 29 | 29 |
} |
| 30 | 30 |
|
| ... | ... |
@@ -304,7 +305,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con |
| 304 | 304 |
if b, err = json.Marshal(w); err != nil {
|
| 305 | 305 |
return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
| 306 | 306 |
} |
| 307 |
- c, h, err = runconfig.DecodeContainerConfig(bytes.NewReader(b)) |
|
| 307 |
+ c, h, _, err = runconfig.DecodeContainerConfig(bytes.NewReader(b)) |
|
| 308 | 308 |
if err != nil {
|
| 309 | 309 |
return nil, nil, fmt.Errorf("Error parsing %s: %v", string(b), err)
|
| 310 | 310 |
} |
| ... | ... |
@@ -349,7 +350,7 @@ func setupPlatformVolume(u []string, w []string) ([]string, string) {
|
| 349 | 349 |
func TestParseWithMacAddress(t *testing.T) {
|
| 350 | 350 |
invalidMacAddress := "--mac-address=invalidMacAddress" |
| 351 | 351 |
validMacAddress := "--mac-address=92:d0:c6:0a:29:33" |
| 352 |
- if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
|
| 352 |
+ if _, _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
|
| 353 | 353 |
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
|
| 354 | 354 |
} |
| 355 | 355 |
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
|
| ... | ... |
@@ -360,7 +361,7 @@ func TestParseWithMacAddress(t *testing.T) {
|
| 360 | 360 |
func TestParseWithMemory(t *testing.T) {
|
| 361 | 361 |
invalidMemory := "--memory=invalid" |
| 362 | 362 |
validMemory := "--memory=1G" |
| 363 |
- if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
|
| 363 |
+ if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
|
| 364 | 364 |
t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
|
| 365 | 365 |
} |
| 366 | 366 |
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
| ... | ... |
@@ -372,7 +373,7 @@ func TestParseWithMemorySwap(t *testing.T) {
|
| 372 | 372 |
invalidMemory := "--memory-swap=invalid" |
| 373 | 373 |
validMemory := "--memory-swap=1G" |
| 374 | 374 |
anotherValidMemory := "--memory-swap=-1" |
| 375 |
- if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
|
| 375 |
+ if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
|
| 376 | 376 |
t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
|
| 377 | 377 |
} |
| 378 | 378 |
if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
|
| ... | ... |
@@ -417,12 +418,12 @@ func TestParseWithExpose(t *testing.T) {
|
| 417 | 417 |
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
|
| 418 | 418 |
} |
| 419 | 419 |
for expose, expectedError := range invalids {
|
| 420 |
- if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 420 |
+ if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 421 | 421 |
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
|
| 422 | 422 |
} |
| 423 | 423 |
} |
| 424 | 424 |
for expose, exposedPorts := range valids {
|
| 425 |
- config, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
|
| 425 |
+ config, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
|
| 426 | 426 |
if err != nil {
|
| 427 | 427 |
t.Fatal(err) |
| 428 | 428 |
} |
| ... | ... |
@@ -436,7 +437,7 @@ func TestParseWithExpose(t *testing.T) {
|
| 436 | 436 |
} |
| 437 | 437 |
} |
| 438 | 438 |
// Merge with actual published port |
| 439 |
- config, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
|
| 439 |
+ config, _, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
|
| 440 | 440 |
if err != nil {
|
| 441 | 441 |
t.Fatal(err) |
| 442 | 442 |
} |
| ... | ... |
@@ -475,7 +476,7 @@ func TestParseDevice(t *testing.T) {
|
| 475 | 475 |
}, |
| 476 | 476 |
} |
| 477 | 477 |
for device, deviceMapping := range valids {
|
| 478 |
- _, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
|
| 478 |
+ _, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
|
| 479 | 479 |
if err != nil {
|
| 480 | 480 |
t.Fatal(err) |
| 481 | 481 |
} |
| ... | ... |
@@ -491,11 +492,11 @@ func TestParseDevice(t *testing.T) {
|
| 491 | 491 |
|
| 492 | 492 |
func TestParseModes(t *testing.T) {
|
| 493 | 493 |
// ipc ko |
| 494 |
- if _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
|
| 494 |
+ if _, _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
|
| 495 | 495 |
t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
|
| 496 | 496 |
} |
| 497 | 497 |
// ipc ok |
| 498 |
- _, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
|
| 498 |
+ _, hostconfig, _, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
|
| 499 | 499 |
if err != nil {
|
| 500 | 500 |
t.Fatal(err) |
| 501 | 501 |
} |
| ... | ... |
@@ -503,11 +504,11 @@ func TestParseModes(t *testing.T) {
|
| 503 | 503 |
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
| 504 | 504 |
} |
| 505 | 505 |
// pid ko |
| 506 |
- if _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
|
| 506 |
+ if _, _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
|
| 507 | 507 |
t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
|
| 508 | 508 |
} |
| 509 | 509 |
// pid ok |
| 510 |
- _, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
|
| 510 |
+ _, hostconfig, _, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
|
| 511 | 511 |
if err != nil {
|
| 512 | 512 |
t.Fatal(err) |
| 513 | 513 |
} |
| ... | ... |
@@ -515,11 +516,11 @@ func TestParseModes(t *testing.T) {
|
| 515 | 515 |
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
| 516 | 516 |
} |
| 517 | 517 |
// uts ko |
| 518 |
- if _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
|
| 518 |
+ if _, _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
|
| 519 | 519 |
t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
|
| 520 | 520 |
} |
| 521 | 521 |
// uts ok |
| 522 |
- _, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
|
| 522 |
+ _, hostconfig, _, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
|
| 523 | 523 |
if err != nil {
|
| 524 | 524 |
t.Fatal(err) |
| 525 | 525 |
} |
| ... | ... |
@@ -527,11 +528,11 @@ func TestParseModes(t *testing.T) {
|
| 527 | 527 |
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
| 528 | 528 |
} |
| 529 | 529 |
// shm-size ko |
| 530 |
- if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
|
| 530 |
+ if _, _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
|
| 531 | 531 |
t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
|
| 532 | 532 |
} |
| 533 | 533 |
// shm-size ok |
| 534 |
- _, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
|
| 534 |
+ _, hostconfig, _, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
|
| 535 | 535 |
if err != nil {
|
| 536 | 536 |
t.Fatal(err) |
| 537 | 537 |
} |
| ... | ... |
@@ -560,12 +561,12 @@ func TestParseRestartPolicy(t *testing.T) {
|
| 560 | 560 |
}, |
| 561 | 561 |
} |
| 562 | 562 |
for restart, expectedError := range invalids {
|
| 563 |
- if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 563 |
+ if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
|
| 564 | 564 |
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
| 565 | 565 |
} |
| 566 | 566 |
} |
| 567 | 567 |
for restart, expected := range valids {
|
| 568 |
- _, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
|
| 568 |
+ _, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
|
| 569 | 569 |
if err != nil {
|
| 570 | 570 |
t.Fatal(err) |
| 571 | 571 |
} |
| ... | ... |
@@ -577,11 +578,11 @@ func TestParseRestartPolicy(t *testing.T) {
|
| 577 | 577 |
|
| 578 | 578 |
func TestParseLoggingOpts(t *testing.T) {
|
| 579 | 579 |
// logging opts ko |
| 580 |
- if _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "Invalid logging opts for driver none" {
|
|
| 580 |
+ if _, _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "Invalid logging opts for driver none" {
|
|
| 581 | 581 |
t.Fatalf("Expected an error with message 'Invalid logging opts for driver none', got %v", err)
|
| 582 | 582 |
} |
| 583 | 583 |
// logging opts ok |
| 584 |
- _, hostconfig, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
|
| 584 |
+ _, hostconfig, _, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
|
| 585 | 585 |
if err != nil {
|
| 586 | 586 |
t.Fatal(err) |
| 587 | 587 |
} |
| ... | ... |
@@ -596,18 +597,18 @@ func TestParseEnvfileVariables(t *testing.T) {
|
| 596 | 596 |
e = "open nonexistent: The system cannot find the file specified." |
| 597 | 597 |
} |
| 598 | 598 |
// env ko |
| 599 |
- if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 599 |
+ if _, _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 600 | 600 |
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
| 601 | 601 |
} |
| 602 | 602 |
// env ok |
| 603 |
- config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
|
| 603 |
+ config, _, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
|
| 604 | 604 |
if err != nil {
|
| 605 | 605 |
t.Fatal(err) |
| 606 | 606 |
} |
| 607 | 607 |
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
| 608 | 608 |
t.Fatalf("Expected a a config with [ENV1=value1], got %v", config.Env)
|
| 609 | 609 |
} |
| 610 |
- config, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
|
| 610 |
+ config, _, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
|
| 611 | 611 |
if err != nil {
|
| 612 | 612 |
t.Fatal(err) |
| 613 | 613 |
} |
| ... | ... |
@@ -622,18 +623,18 @@ func TestParseLabelfileVariables(t *testing.T) {
|
| 622 | 622 |
e = "open nonexistent: The system cannot find the file specified." |
| 623 | 623 |
} |
| 624 | 624 |
// label ko |
| 625 |
- if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 625 |
+ if _, _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
|
| 626 | 626 |
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
| 627 | 627 |
} |
| 628 | 628 |
// label ok |
| 629 |
- config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
|
| 629 |
+ config, _, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
|
| 630 | 630 |
if err != nil {
|
| 631 | 631 |
t.Fatal(err) |
| 632 | 632 |
} |
| 633 | 633 |
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
| 634 | 634 |
t.Fatalf("Expected a a config with [LABEL1:value1], got %v", config.Labels)
|
| 635 | 635 |
} |
| 636 |
- config, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
|
| 636 |
+ config, _, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
|
| 637 | 637 |
if err != nil {
|
| 638 | 638 |
t.Fatal(err) |
| 639 | 639 |
} |
| ... | ... |
@@ -643,7 +644,7 @@ func TestParseLabelfileVariables(t *testing.T) {
|
| 643 | 643 |
} |
| 644 | 644 |
|
| 645 | 645 |
func TestParseEntryPoint(t *testing.T) {
|
| 646 |
- config, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
|
| 646 |
+ config, _, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
|
| 647 | 647 |
if err != nil {
|
| 648 | 648 |
t.Fatal(err) |
| 649 | 649 |
} |