mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Allow user to choose the IP address for the container
Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
19b063e740
commit
2bb3fc1bc5
26 changed files with 412 additions and 157 deletions
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/engine-api/client"
|
"github.com/docker/engine-api/client"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (cli *DockerCli) pullImage(image string) error {
|
func (cli *DockerCli) pullImage(image string) error {
|
||||||
|
@ -79,7 +80,7 @@ func newCIDFile(path string) (*cidFile, error) {
|
||||||
return &cidFile{path: path, file: f}, nil
|
return &cidFile{path: path, file: f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
func (cli *DockerCli) createContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
|
||||||
var containerIDFile *cidFile
|
var containerIDFile *cidFile
|
||||||
if cidfile != "" {
|
if cidfile != "" {
|
||||||
var err error
|
var err error
|
||||||
|
@ -107,7 +108,8 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
|
||||||
}
|
}
|
||||||
|
|
||||||
//create the container
|
//create the container
|
||||||
response, err := cli.client.ContainerCreate(config, hostConfig, nil, name)
|
response, err := cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
||||||
|
|
||||||
//if image not found try to pull it
|
//if image not found try to pull it
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if client.IsErrImageNotFound(err) {
|
if client.IsErrImageNotFound(err) {
|
||||||
|
@ -124,7 +126,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
|
||||||
}
|
}
|
||||||
// Retry
|
// Retry
|
||||||
var retryErr error
|
var retryErr error
|
||||||
response, retryErr = cli.client.ContainerCreate(config, hostConfig, nil, name)
|
response, retryErr = cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
||||||
if retryErr != nil {
|
if retryErr != nil {
|
||||||
return nil, retryErr
|
return nil, retryErr
|
||||||
}
|
}
|
||||||
|
@ -156,7 +158,8 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
||||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||||
)
|
)
|
||||||
|
|
||||||
config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
cmd.ReportError(err.Error(), true)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -165,7 +168,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,15 +107,22 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
|
||||||
|
|
||||||
// CmdNetworkConnect connects a container to a network
|
// CmdNetworkConnect connects a container to a network
|
||||||
//
|
//
|
||||||
// Usage: docker network connect <NETWORK> <CONTAINER>
|
// Usage: docker network connect [OPTIONS] <NETWORK> <CONTAINER>
|
||||||
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
|
||||||
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
|
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
|
||||||
cmd.Require(flag.Exact, 2)
|
flIPAddress := cmd.String([]string{"-ip"}, "", "IP Address")
|
||||||
|
flIPv6Address := cmd.String([]string{"-ip6"}, "", "IPv6 Address")
|
||||||
|
cmd.Require(flag.Min, 2)
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
if err := cmd.ParseFlags(args, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
epConfig := &network.EndpointSettings{
|
||||||
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), nil)
|
IPAMConfig: &network.EndpointIPAMConfig{
|
||||||
|
IPv4Address: *flIPAddress,
|
||||||
|
IPv6Address: *flIPv6Address,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), epConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdNetworkDisconnect disconnects a container from a network
|
// CmdNetworkDisconnect disconnects a container from a network
|
||||||
|
|
|
@ -82,7 +82,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
||||||
)
|
)
|
||||||
|
|
||||||
config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||||
|
|
||||||
// just in case the Parse does not exit
|
// just in case the Parse does not exit
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
cmd.ReportError(err.Error(), true)
|
||||||
|
@ -145,7 +146,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize()
|
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = cli.getTtySize()
|
||||||
}
|
}
|
||||||
|
|
||||||
createResponse, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
createResponse, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.ReportError(err.Error(), true)
|
cmd.ReportError(err.Error(), true)
|
||||||
return runStartContainerErr(err)
|
return runStartContainerErr(err)
|
||||||
|
|
|
@ -332,7 +332,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
_, hostConfig, _, err := runconfig.DecodeContainerConfig(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -358,7 +358,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||||
|
|
||||||
name := r.Form.Get("name")
|
name := r.Form.Get("name")
|
||||||
|
|
||||||
config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
config, hostConfig, networkingConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -366,10 +366,11 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
||||||
adjustCPUShares := version.LessThan("1.19")
|
adjustCPUShares := version.LessThan("1.19")
|
||||||
|
|
||||||
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
||||||
Name: name,
|
Name: name,
|
||||||
Config: config,
|
Config: config,
|
||||||
HostConfig: hostConfig,
|
HostConfig: hostConfig,
|
||||||
AdjustCPUShares: adjustCPUShares,
|
NetworkingConfig: networkingConfig,
|
||||||
|
AdjustCPUShares: adjustCPUShares,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.
|
||||||
pause = true
|
pause = true
|
||||||
}
|
}
|
||||||
|
|
||||||
c, _, err := runconfig.DecodeContainerConfig(r.Body)
|
c, _, _, err := runconfig.DecodeContainerConfig(r.Body)
|
||||||
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
if err != nil && err != io.EOF { //Do not fail if body is empty.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/docker/engine-api/types/network"
|
"github.com/docker/engine-api/types/network"
|
||||||
|
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +14,7 @@ type Backend interface {
|
||||||
GetAllNetworks() []libnetwork.Network
|
GetAllNetworks() []libnetwork.Network
|
||||||
CreateNetwork(name, driver string, ipam network.IPAM,
|
CreateNetwork(name, driver string, ipam network.IPAM,
|
||||||
options map[string]string) (libnetwork.Network, error)
|
options map[string]string) (libnetwork.Network, error)
|
||||||
ConnectContainerToNetwork(containerName, networkName string) error
|
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
|
||||||
DisconnectContainerFromNetwork(containerName string,
|
DisconnectContainerFromNetwork(containerName string,
|
||||||
network libnetwork.Network) error
|
network libnetwork.Network) error
|
||||||
NetworkControllerEnabled() bool
|
NetworkControllerEnabled() bool
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.backend.ConnectContainerToNetwork(connect.Container, nw.Name())
|
return n.backend.ConnectContainerToNetwork(connect.Container, nw.Name(), connect.EndpointConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
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) ([]
|
||||||
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
|
createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
|
||||||
|
ipam := epConfig.IPAMConfig
|
||||||
|
if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") {
|
||||||
|
createOptions = append(createOptions,
|
||||||
|
libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Other configs are applicable only for the endpoint in the network
|
// Other configs are applicable only for the endpoint in the network
|
||||||
// to which container was connected to on docker run.
|
// to which container was connected to on docker run.
|
||||||
if n.Name() != container.HostConfig.NetworkMode.NetworkName() &&
|
if n.Name() != container.HostConfig.NetworkMode.NetworkName() &&
|
||||||
|
|
|
@ -503,7 +503,10 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
|
||||||
return runconfig.ErrConflictNoNetwork
|
return runconfig.ErrConflictNoNetwork
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container.NetworkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings)
|
|
||||||
|
if _, ok := container.NetworkSettings.Networks[n.Name()]; !ok {
|
||||||
|
container.NetworkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -562,7 +565,12 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateContainerNetworkSettings update the network settings
|
// updateContainerNetworkSettings update the network settings
|
||||||
func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container) error {
|
func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container, endpointsConfig map[string]*networktypes.EndpointSettings) error {
|
||||||
|
var (
|
||||||
|
n libnetwork.Network
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
mode := container.HostConfig.NetworkMode
|
mode := container.HostConfig.NetworkMode
|
||||||
if container.Config.NetworkDisabled || mode.IsContainer() {
|
if container.Config.NetworkDisabled || mode.IsContainer() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -573,14 +581,35 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
|
||||||
networkName = daemon.netController.Config().Daemon.DefaultNetwork
|
networkName = daemon.netController.Config().Daemon.DefaultNetwork
|
||||||
}
|
}
|
||||||
if mode.IsUserDefined() {
|
if mode.IsUserDefined() {
|
||||||
n, err := daemon.FindNetwork(networkName)
|
n, err = daemon.FindNetwork(networkName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
networkName = n.Name()
|
networkName = n.Name()
|
||||||
}
|
}
|
||||||
container.NetworkSettings.Networks = make(map[string]*networktypes.EndpointSettings)
|
if container.NetworkSettings == nil {
|
||||||
container.NetworkSettings.Networks[networkName] = new(networktypes.EndpointSettings)
|
container.NetworkSettings = &network.Settings{}
|
||||||
|
}
|
||||||
|
if endpointsConfig != nil {
|
||||||
|
container.NetworkSettings.Networks = endpointsConfig
|
||||||
|
}
|
||||||
|
if container.NetworkSettings.Networks == nil {
|
||||||
|
container.NetworkSettings.Networks = make(map[string]*networktypes.EndpointSettings)
|
||||||
|
container.NetworkSettings.Networks[networkName] = new(networktypes.EndpointSettings)
|
||||||
|
}
|
||||||
|
if !mode.IsUserDefined() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Make sure to internally store the per network endpoint config by network name
|
||||||
|
if _, ok := container.NetworkSettings.Networks[networkName]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if nwConfig, ok := container.NetworkSettings.Networks[n.ID()]; ok {
|
||||||
|
container.NetworkSettings.Networks[networkName] = nwConfig
|
||||||
|
delete(container.NetworkSettings.Networks, n.ID())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,15 +627,15 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := daemon.updateContainerNetworkSettings(container)
|
err := daemon.updateContainerNetworkSettings(container, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
updateSettings = true
|
updateSettings = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for n := range container.NetworkSettings.Networks {
|
for n, nConf := range container.NetworkSettings.Networks {
|
||||||
if err := daemon.connectToNetwork(container, n, updateSettings); err != nil {
|
if err := daemon.connectToNetwork(container, n, nConf, updateSettings); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -626,12 +655,65 @@ func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwo
|
||||||
return sb
|
return sb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasUserDefinedIPAddress returns whether the passed endpoint configuration contains IP address configuration
|
||||||
|
func hasUserDefinedIPAddress(epConfig *networktypes.EndpointSettings) bool {
|
||||||
|
return epConfig != nil && epConfig.IPAMConfig != nil && (len(epConfig.IPAMConfig.IPv4Address) > 0 || len(epConfig.IPAMConfig.IPv6Address) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User specified ip address is acceptable only for networks with user specified subnets.
|
||||||
|
func validateNetworkingConfig(n libnetwork.Network, epConfig *networktypes.EndpointSettings) error {
|
||||||
|
if !hasUserDefinedIPAddress(epConfig) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, nwIPv4Configs, nwIPv6Configs := n.Info().IpamConfig()
|
||||||
|
for _, s := range []struct {
|
||||||
|
ipConfigured bool
|
||||||
|
subnetConfigs []*libnetwork.IpamConf
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ipConfigured: len(epConfig.IPAMConfig.IPv4Address) > 0,
|
||||||
|
subnetConfigs: nwIPv4Configs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ipConfigured: len(epConfig.IPAMConfig.IPv6Address) > 0,
|
||||||
|
subnetConfigs: nwIPv6Configs,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if s.ipConfigured {
|
||||||
|
foundSubnet := false
|
||||||
|
for _, cfg := range s.subnetConfigs {
|
||||||
|
if len(cfg.PreferredPool) > 0 {
|
||||||
|
foundSubnet = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundSubnet {
|
||||||
|
return runconfig.ErrUnsupportedNetworkNoSubnetAndIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanOperationalData resets the operational data from the passed endpoint settings
|
||||||
|
func cleanOperationalData(es *networktypes.EndpointSettings) {
|
||||||
|
es.EndpointID = ""
|
||||||
|
es.Gateway = ""
|
||||||
|
es.IPAddress = ""
|
||||||
|
es.IPPrefixLen = 0
|
||||||
|
es.IPv6Gateway = ""
|
||||||
|
es.GlobalIPv6Address = ""
|
||||||
|
es.GlobalIPv6PrefixLen = 0
|
||||||
|
es.MacAddress = ""
|
||||||
|
}
|
||||||
|
|
||||||
// ConnectToNetwork connects a container to a network
|
// ConnectToNetwork connects a container to a network
|
||||||
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error {
|
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
||||||
if !container.Running {
|
if !container.Running {
|
||||||
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
return derr.ErrorCodeNotRunning.WithArgs(container.ID)
|
||||||
}
|
}
|
||||||
if err := daemon.connectToNetwork(container, idOrName, true); err != nil {
|
if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := container.ToDiskLocking(); err != nil {
|
if err := container.ToDiskLocking(); err != nil {
|
||||||
|
@ -640,11 +722,15 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, updateSettings bool) (err error) {
|
func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
|
||||||
if container.HostConfig.NetworkMode.IsContainer() {
|
if container.HostConfig.NetworkMode.IsContainer() {
|
||||||
return runconfig.ErrConflictSharedNetwork
|
return runconfig.ErrConflictSharedNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) {
|
||||||
|
return runconfig.ErrUnsupportedNetworkAndIP
|
||||||
|
}
|
||||||
|
|
||||||
if containertypes.NetworkMode(idOrName).IsBridge() &&
|
if containertypes.NetworkMode(idOrName).IsBridge() &&
|
||||||
daemon.configStore.DisableBridge {
|
daemon.configStore.DisableBridge {
|
||||||
container.Config.NetworkDisabled = true
|
container.Config.NetworkDisabled = true
|
||||||
|
@ -658,12 +744,20 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validateNetworkingConfig(n, endpointConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if updateSettings {
|
if updateSettings {
|
||||||
if err := daemon.updateNetworkSettings(container, n); err != nil {
|
if err := daemon.updateNetworkSettings(container, n); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if endpointConfig != nil {
|
||||||
|
container.NetworkSettings.Networks[n.Name()] = endpointConfig
|
||||||
|
}
|
||||||
|
|
||||||
ep, err := container.GetEndpointInNetwork(n)
|
ep, err := container.GetEndpointInNetwork(n)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Conflict. A container with name %q is already connected to network %s.", strings.TrimPrefix(container.Name, "/"), idOrName)
|
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) {
|
||||||
|
|
||||||
sid := container.NetworkSettings.SandboxID
|
sid := container.NetworkSettings.SandboxID
|
||||||
settings := container.NetworkSettings.Networks
|
settings := container.NetworkSettings.Networks
|
||||||
|
if sid == "" || len(settings) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var networks []libnetwork.Network
|
var networks []libnetwork.Network
|
||||||
for n := range settings {
|
for n, epSettings := range settings {
|
||||||
if nw, err := daemon.FindNetwork(n); err == nil {
|
if nw, err := daemon.FindNetwork(n); err == nil {
|
||||||
networks = append(networks, nw)
|
networks = append(networks, nw)
|
||||||
}
|
}
|
||||||
settings[n] = &networktypes.EndpointSettings{}
|
cleanOperationalData(epSettings)
|
||||||
}
|
|
||||||
|
|
||||||
container.NetworkSettings = &network.Settings{Networks: settings}
|
|
||||||
|
|
||||||
if sid == "" || len(settings) == 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sb, err := daemon.netController.SandboxByID(sid)
|
sb, err := daemon.netController.SandboxByID(sid)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/docker/docker/daemon/execdriver/windows"
|
"github.com/docker/docker/daemon/execdriver/windows"
|
||||||
derr "github.com/docker/docker/errors"
|
derr "github.com/docker/docker/errors"
|
||||||
"github.com/docker/docker/layer"
|
"github.com/docker/docker/layer"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateContainerNetworkSettings update the network settings
|
// updateContainerNetworkSettings update the network settings
|
||||||
func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container) error {
|
func (daemon *Daemon) updateContainerNetworkSettings(container *container.Container, endpointsConfig map[string]*networktypes.EndpointSettings) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ func (daemon *Daemon) initializeNetworking(container *container.Container) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectToNetwork connects a container to the network
|
// ConnectToNetwork connects a container to the network
|
||||||
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string) error {
|
func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
volumestore "github.com/docker/docker/volume/store"
|
volumestore "github.com/docker/docker/volume/store"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
containertypes "github.com/docker/engine-api/types/container"
|
containertypes "github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/opencontainers/runc/libcontainer/label"
|
"github.com/opencontainers/runc/libcontainer/label"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -108,7 +109,12 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := daemon.updateContainerNetworkSettings(container); err != nil {
|
var endpointsConfigs map[string]*networktypes.EndpointSettings
|
||||||
|
if params.NetworkingConfig != nil {
|
||||||
|
endpointsConfigs = params.NetworkingConfig.EndpointsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := daemon.updateContainerNetworkSettings(container, endpointsConfigs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,12 +150,12 @@ func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnet
|
||||||
// ConnectContainerToNetwork connects the given container to the given
|
// ConnectContainerToNetwork connects the given container to the given
|
||||||
// network. If either cannot be found, an err is returned. If the
|
// network. If either cannot be found, an err is returned. If the
|
||||||
// network cannot be set up, an err is returned.
|
// network cannot be set up, an err is returned.
|
||||||
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string) error {
|
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error {
|
||||||
container, err := daemon.GetContainer(containerName)
|
container, err := daemon.GetContainer(containerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return daemon.ConnectToNetwork(container, networkName)
|
return daemon.ConnectToNetwork(container, networkName, endpointConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisconnectContainerFromNetwork disconnects the given container from
|
// DisconnectContainerFromNetwork disconnects the given container from
|
||||||
|
|
|
@ -110,6 +110,8 @@ This section lists each version from latest to oldest. Each listing includes a
|
||||||
* `POST /containers/create` now allows you to set a read/write rate limit for a
|
* `POST /containers/create` now allows you to set a read/write rate limit for a
|
||||||
device (in bytes per second or IO per second).
|
device (in bytes per second or IO per second).
|
||||||
* `GET /networks` now supports filtering by `name`, `id` and `type`.
|
* `GET /networks` now supports filtering by `name`, `id` and `type`.
|
||||||
|
* `POST /containers/create` now allows you to set the static IPv4 and/or IPv6 address for the container.
|
||||||
|
* `POST /networks/(id)/connect` now allows you to set the static IPv4 and/or IPv6 address for the container.
|
||||||
|
|
||||||
### v1.21 API changes
|
### v1.21 API changes
|
||||||
|
|
||||||
|
|
|
@ -3031,7 +3031,13 @@ POST /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30/
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"Container":"3613f73ba0e4"
|
"Container":"3613f73ba0e4",
|
||||||
|
"endpoint_config": {
|
||||||
|
"test_nw": {
|
||||||
|
"IPv4Address":"172.24.56.89",
|
||||||
|
"IPv6Address":"2001:db8::5689"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,18 @@ You can also use the `docker run --net=<network-name>` option to start a contain
|
||||||
$ docker run -itd --net=multi-host-network busybox
|
$ docker run -itd --net=multi-host-network busybox
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can specify the IP address you want to be assigned to the container's interface.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker network connect multi-host-network --ip 10.10.36.122 container2
|
||||||
|
```
|
||||||
|
|
||||||
You can pause, restart, and stop containers that are connected to a network.
|
You can pause, restart, and stop containers that are connected to a network.
|
||||||
Paused containers remain connected and a revealed by a `network inspect`. When
|
Paused containers remain connected and a revealed by a `network inspect`. When
|
||||||
the container is stopped, it does not appear on the network until you restart
|
the container is stopped, it does not appear on the network until you restart
|
||||||
it. The container's IP address is not guaranteed to remain the same when a
|
it. The container's IP address is not guaranteed to remain the same when a
|
||||||
stopped container rejoins the network.
|
stopped container rejoins the network, unless you specified one when you run
|
||||||
|
`docker network connect` command.
|
||||||
|
|
||||||
To verify the container is connected, use the `docker network inspect` command. Use `docker network disconnect` to remove a container from the network.
|
To verify the container is connected, use the `docker network inspect` command. Use `docker network disconnect` to remove a container from the network.
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,8 @@ parent = "smn_cli"
|
||||||
--log-opt=[] Log driver specific options
|
--log-opt=[] Log driver specific options
|
||||||
-m, --memory="" Memory limit
|
-m, --memory="" Memory limit
|
||||||
--mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33)
|
--mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33)
|
||||||
|
--ip="" Container IPv4 address (e.g. 172.30.100.104)
|
||||||
|
--ip6="" Container IPv6 address (e.g. 2001:db8::33)
|
||||||
--memory-reservation="" Memory soft limit
|
--memory-reservation="" Memory soft limit
|
||||||
--memory-swap="" A positive integer equal to memory plus swap. Specify -1 to enable unlimited swap.
|
--memory-swap="" A positive integer equal to memory plus swap. Specify -1 to enable unlimited swap.
|
||||||
--memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
|
--memory-swappiness="" Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
|
||||||
|
|
|
@ -275,6 +275,8 @@ of the containers.
|
||||||
'<network-name>|<network-id>': connect to a user-defined network
|
'<network-name>|<network-id>': connect to a user-defined network
|
||||||
--add-host="" : Add a line to /etc/hosts (host:IP)
|
--add-host="" : Add a line to /etc/hosts (host:IP)
|
||||||
--mac-address="" : Sets the container's Ethernet device's MAC address
|
--mac-address="" : Sets the container's Ethernet device's MAC address
|
||||||
|
--ip="" : Sets the container's Ethernet device's IPv4 address
|
||||||
|
--ip6="" : Sets the container's Ethernet device's IPv6 address
|
||||||
|
|
||||||
By default, all containers have networking enabled and they can make any
|
By default, all containers have networking enabled and they can make any
|
||||||
outgoing connections. The operator can completely disable networking
|
outgoing connections. The operator can completely disable networking
|
||||||
|
|
|
@ -115,8 +115,8 @@ $ docker run -itd --name=container2 busybox
|
||||||
Then create an isolated, `bridge` network to test with.
|
Then create an isolated, `bridge` network to test with.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker network create -d bridge isolated_nw
|
$ docker network create -d bridge --subnet 172.25.0.0/16 isolated_nw
|
||||||
f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a
|
06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8
|
||||||
```
|
```
|
||||||
|
|
||||||
Connect `container2` to the network and then `inspect` the network to verify the connection:
|
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
|
||||||
```
|
```
|
||||||
$ docker network connect isolated_nw container2
|
$ docker network connect isolated_nw container2
|
||||||
$ docker network inspect isolated_nw
|
$ docker network inspect isolated_nw
|
||||||
[[
|
[
|
||||||
{
|
{
|
||||||
"Name": "isolated_nw",
|
"Name": "isolated_nw",
|
||||||
"Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a",
|
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
|
||||||
"Scope": "local",
|
"Scope": "local",
|
||||||
"Driver": "bridge",
|
"Driver": "bridge",
|
||||||
"IPAM": {
|
"IPAM": {
|
||||||
"Driver": "default",
|
"Driver": "default",
|
||||||
"Config": [
|
"Config": [
|
||||||
{}
|
{
|
||||||
|
"Subnet": "172.25.0.0/16"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Containers": {
|
"Containers": {
|
||||||
"498eaaaf328e1018042c04b2de04036fc04719a6e39a097a4f4866043a2c2152": {
|
"90e1f3ec71caf82ae776a827e0712a68a110a3f175954e5bd4222fd142ac9428": {
|
||||||
"EndpointID": "0e24479cfaafb029104999b4e120858a07b19b1b6d956ae56811033e45d68ad9",
|
"Name": "container2",
|
||||||
"MacAddress": "02:42:ac:15:00:02",
|
"EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
|
||||||
"IPv4Address": "172.21.0.2/16",
|
"MacAddress": "02:42:ac:19:00:02",
|
||||||
|
"IPv4Address": "172.25.0.2/16",
|
||||||
"IPv6Address": ""
|
"IPv6Address": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -150,20 +153,28 @@ $ docker network inspect isolated_nw
|
||||||
```
|
```
|
||||||
|
|
||||||
You can see that the Engine automatically assigns an IP address to `container2`.
|
You can see that the Engine automatically assigns an IP address to `container2`.
|
||||||
If you had specified a `--subnetwork` when creating your network, the network
|
Given we specified a `--subnet` when creating the network, Engine picked
|
||||||
would have used that addressing. Now, start a third container and connect it to
|
an address from that same subnet. Now, start a third container and connect it to
|
||||||
the network on launch using the `docker run` command's `--net` option:
|
the network on launch using the `docker run` command's `--net` option:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker run --net=isolated_nw -itd --name=container3 busybox
|
$ docker run --net=isolated_nw --ip=172.25.3.3 -itd --name=container3 busybox
|
||||||
c282ca437ee7e926a7303a64fc04109740208d2c20e442366139322211a6481c
|
467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551
|
||||||
```
|
```
|
||||||
|
|
||||||
|
As you can see you were able to specify the ip address for your container.
|
||||||
|
As long as the network to which the container is connecting was created with
|
||||||
|
a user specified subnet, you will be able to select the IPv4 and/or IPv6 address(es)
|
||||||
|
for your container when executing `docker run` and `docker network connect` commands.
|
||||||
|
The selected IP address is part of the container networking configuration and will be
|
||||||
|
preserved across container reload. The feature is only available on user defined networks,
|
||||||
|
because they guarantee their subnets configuration does not change across daemon reload.
|
||||||
|
|
||||||
Now, inspect the network resources used by `container3`.
|
Now, inspect the network resources used by `container3`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container3
|
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container3
|
||||||
{"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"}}
|
{"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"}}
|
||||||
```
|
```
|
||||||
Repeat this command for `container2`. If you have Python installed, you can pretty print the output.
|
Repeat this command for `container2`. If you have Python installed, you can pretty print the output.
|
||||||
|
|
||||||
|
@ -171,24 +182,26 @@ Repeat this command for `container2`. If you have Python installed, you can pret
|
||||||
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | python -m json.tool
|
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | python -m json.tool
|
||||||
{
|
{
|
||||||
"bridge": {
|
"bridge": {
|
||||||
"EndpointID": "281b5ead415cf48a6a84fd1a6504342c76e9091fe09b4fdbcc4a01c30b0d3c5b",
|
"EndpointID": "0099f9efb5a3727f6a554f176b1e96fca34cae773da68b3b6a26d046c12cb365",
|
||||||
"Gateway": "172.17.0.1",
|
"Gateway": "172.17.0.1",
|
||||||
"GlobalIPv6Address": "",
|
"GlobalIPv6Address": "",
|
||||||
"GlobalIPv6PrefixLen": 0,
|
"GlobalIPv6PrefixLen": 0,
|
||||||
|
"IPAMConfig": null,
|
||||||
"IPAddress": "172.17.0.3",
|
"IPAddress": "172.17.0.3",
|
||||||
"IPPrefixLen": 16,
|
"IPPrefixLen": 16,
|
||||||
"IPv6Gateway": "",
|
"IPv6Gateway": "",
|
||||||
"MacAddress": "02:42:ac:11:00:03"
|
"MacAddress": "02:42:ac:11:00:03"
|
||||||
},
|
},
|
||||||
"isolated_nw": {
|
"isolated_nw": {
|
||||||
"EndpointID": "0e24479cfaafb029104999b4e120858a07b19b1b6d956ae56811033e45d68ad9",
|
"EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
|
||||||
"Gateway": "172.21.0.1",
|
"Gateway": "172.25.0.1",
|
||||||
"GlobalIPv6Address": "",
|
"GlobalIPv6Address": "",
|
||||||
"GlobalIPv6PrefixLen": 0,
|
"GlobalIPv6PrefixLen": 0,
|
||||||
"IPAddress": "172.21.0.2",
|
"IPAMConfig": null,
|
||||||
|
"IPAddress": "172.25.0.2",
|
||||||
"IPPrefixLen": 16,
|
"IPPrefixLen": 16,
|
||||||
"IPv6Gateway": "",
|
"IPv6Gateway": "",
|
||||||
"MacAddress": "02:42:ac:15:00:02"
|
"MacAddress": "02:42:ac:19:00:02"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -223,8 +236,8 @@ eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
|
||||||
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
|
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
|
||||||
|
|
||||||
eth1 Link encap:Ethernet HWaddr 02:42:AC:15:00:02
|
eth1 Link encap:Ethernet HWaddr 02:42:AC:15:00:02
|
||||||
inet addr:172.21.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
|
inet addr:172.25.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
|
||||||
inet6 addr: fe80::42:acff:fe15:2/64 Scope:Link
|
inet6 addr: fe80::42:acff:fe19:2/64 Scope:Link
|
||||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||||
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
|
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
|
||||||
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
|
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
|
||||||
|
@ -252,19 +265,19 @@ fe00::0 ip6-localnet
|
||||||
ff00::0 ip6-mcastprefix
|
ff00::0 ip6-mcastprefix
|
||||||
ff02::1 ip6-allnodes
|
ff02::1 ip6-allnodes
|
||||||
ff02::2 ip6-allrouters
|
ff02::2 ip6-allrouters
|
||||||
172.21.0.3 container3
|
172.21.3.3 container3
|
||||||
172.21.0.3 container3.isolated_nw
|
172.21.3.3 container3.isolated_nw
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
/ # ping -w 4 container3
|
/ # ping -w 4 container3
|
||||||
PING container3 (172.21.0.3): 56 data bytes
|
PING container3 (172.25.3.3): 56 data bytes
|
||||||
64 bytes from 172.21.0.3: seq=0 ttl=64 time=0.070 ms
|
64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.070 ms
|
||||||
64 bytes from 172.21.0.3: seq=1 ttl=64 time=0.080 ms
|
64 bytes from 172.25.3.3: seq=1 ttl=64 time=0.080 ms
|
||||||
64 bytes from 172.21.0.3: seq=2 ttl=64 time=0.080 ms
|
64 bytes from 172.25.3.3: seq=2 ttl=64 time=0.080 ms
|
||||||
64 bytes from 172.21.0.3: seq=3 ttl=64 time=0.097 ms
|
64 bytes from 172.25.3.3: seq=3 ttl=64 time=0.097 ms
|
||||||
|
|
||||||
--- container3 ping statistics ---
|
--- container3 ping statistics ---
|
||||||
4 packets transmitted, 4 packets received, 0% packet loss
|
4 packets transmitted, 4 packets received, 0% packet loss
|
||||||
|
@ -342,23 +355,26 @@ docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | pytho
|
||||||
|
|
||||||
|
|
||||||
$ docker network inspect isolated_nw
|
$ docker network inspect isolated_nw
|
||||||
[[
|
[
|
||||||
{
|
{
|
||||||
"Name": "isolated_nw",
|
"Name": "isolated_nw",
|
||||||
"Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a",
|
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
|
||||||
"Scope": "local",
|
"Scope": "local",
|
||||||
"Driver": "bridge",
|
"Driver": "bridge",
|
||||||
"IPAM": {
|
"IPAM": {
|
||||||
"Driver": "default",
|
"Driver": "default",
|
||||||
"Config": [
|
"Config": [
|
||||||
{}
|
{
|
||||||
|
"Subnet": "172.25.0.0/16"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Containers": {
|
"Containers": {
|
||||||
"c282ca437ee7e926a7303a64fc04109740208d2c20e442366139322211a6481c": {
|
"467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551": {
|
||||||
"EndpointID": "e5d077f9712a69c6929fdd890df5e7c1c649771a50df5b422f7e68f0ae61e847",
|
"Name": "container3",
|
||||||
"MacAddress": "02:42:ac:15:00:03",
|
"EndpointID": "dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103",
|
||||||
"IPv4Address": "172.21.0.3/16",
|
"MacAddress": "02:42:ac:19:03:03",
|
||||||
|
"IPv4Address": "172.25.3.3/16",
|
||||||
"IPv6Address": ""
|
"IPv6Address": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -393,7 +409,7 @@ lo Link encap:Local Loopback
|
||||||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
||||||
|
|
||||||
/ # ping container3
|
/ # ping container3
|
||||||
PING container3 (172.20.0.1): 56 data bytes
|
PING container3 (172.25.3.3): 56 data bytes
|
||||||
^C
|
^C
|
||||||
--- container3 ping statistics ---
|
--- container3 ping statistics ---
|
||||||
2 packets transmitted, 0 packets received, 100% packet loss
|
2 packets transmitted, 0 packets received, 100% packet loss
|
||||||
|
@ -426,13 +442,15 @@ docker network inspect isolated_nw
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"Name": "isolated_nw",
|
"Name": "isolated_nw",
|
||||||
"Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a",
|
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
|
||||||
"Scope": "local",
|
"Scope": "local",
|
||||||
"Driver": "bridge",
|
"Driver": "bridge",
|
||||||
"IPAM": {
|
"IPAM": {
|
||||||
"Driver": "default",
|
"Driver": "default",
|
||||||
"Config": [
|
"Config": [
|
||||||
{}
|
{
|
||||||
|
"Subnet": "172.25.0.0/16"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Containers": {},
|
"Containers": {},
|
||||||
|
|
|
@ -962,3 +962,71 @@ func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMulipleNetworks(c *chec
|
||||||
c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network"))
|
c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network"))
|
||||||
c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' netwokr"))
|
c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' netwokr"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
|
||||||
|
// create two networks
|
||||||
|
dockerCmd(c, "network", "create", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0")
|
||||||
|
assertNwIsAvailable(c, "n0")
|
||||||
|
|
||||||
|
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")
|
||||||
|
assertNwIsAvailable(c, "n1")
|
||||||
|
|
||||||
|
// run a container on first network specifying the ip addresses
|
||||||
|
dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
|
||||||
|
c.Assert(waitRun("c0"), check.IsNil)
|
||||||
|
verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
|
||||||
|
|
||||||
|
// connect the container to the second network specifying the preferred ip addresses
|
||||||
|
dockerCmd(c, "network", "connect", "--ip", "172.30.55.44", "--ip6", "2001:db8:abcd::5544", "n1", "c0")
|
||||||
|
verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544")
|
||||||
|
|
||||||
|
// Stop and restart the container
|
||||||
|
dockerCmd(c, "stop", "c0")
|
||||||
|
dockerCmd(c, "start", "c0")
|
||||||
|
|
||||||
|
// verify preferred addresses are applied
|
||||||
|
verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
|
||||||
|
verifyIPAddresses(c, "c0", "n1", "172.30.55.44", "2001:db8:abcd::5544")
|
||||||
|
|
||||||
|
// Still it should fail to connect to the default network with a specified IP (whatever ip)
|
||||||
|
out, _, err := dockerCmdWithError("network", "connect", "--ip", "172.21.55.44", "bridge", "c0")
|
||||||
|
c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
||||||
|
c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndIP.Error())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedPreferredIP(c *check.C) {
|
||||||
|
// preferred IP is not supported on predefined networks
|
||||||
|
for _, mode := range []string{"none", "host", "bridge"} {
|
||||||
|
checkUnsupportedNetworkAndIP(c, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// preferred IP is not supported on networks with no user defined subnets
|
||||||
|
dockerCmd(c, "network", "create", "n0")
|
||||||
|
assertNwIsAvailable(c, "n0")
|
||||||
|
|
||||||
|
out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top")
|
||||||
|
c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
||||||
|
c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())
|
||||||
|
|
||||||
|
out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top")
|
||||||
|
c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
||||||
|
c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())
|
||||||
|
|
||||||
|
dockerCmd(c, "network", "rm", "n0")
|
||||||
|
assertNwNotAvailable(c, "n0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUnsupportedNetworkAndIP(c *check.C, nwMode string) {
|
||||||
|
out, _, err := dockerCmdWithError("run", "-d", "--net", nwMode, "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
|
||||||
|
c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
||||||
|
c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndIP.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
|
||||||
|
out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", nwname), cName)
|
||||||
|
c.Assert(strings.TrimSpace(out), check.Equals, ipv4)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.GlobalIPv6Address }}'", nwname), cName)
|
||||||
|
c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
|
||||||
|
}
|
||||||
|
|
|
@ -7,18 +7,19 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/volume"
|
"github.com/docker/docker/volume"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
|
// DecodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
|
||||||
// struct and returns both a Config and an HostConfig struct
|
// struct and returns both a Config and an HostConfig struct
|
||||||
// Be aware this function is not checking whether the resulted structs are nil,
|
// Be aware this function is not checking whether the resulted structs are nil,
|
||||||
// it's your business to do so
|
// it's your business to do so
|
||||||
func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostConfig, error) {
|
func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||||
var w ContainerConfigWrapper
|
var w ContainerConfigWrapper
|
||||||
|
|
||||||
decoder := json.NewDecoder(src)
|
decoder := json.NewDecoder(src)
|
||||||
if err := decoder.Decode(&w); err != nil {
|
if err := decoder.Decode(&w); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hc := w.getHostConfig()
|
hc := w.getHostConfig()
|
||||||
|
@ -33,21 +34,21 @@ func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostCon
|
||||||
|
|
||||||
// Now validate all the volumes and binds
|
// Now validate all the volumes and binds
|
||||||
if err := validateVolumesAndBindSettings(w.Config, hc); err != nil {
|
if err := validateVolumesAndBindSettings(w.Config, hc); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Certain parameters need daemon-side validation that cannot be done
|
// Certain parameters need daemon-side validation that cannot be done
|
||||||
// on the client, as only the daemon knows what is valid for the platform.
|
// on the client, as only the daemon knows what is valid for the platform.
|
||||||
if err := ValidateNetMode(w.Config, hc); err != nil {
|
if err := ValidateNetMode(w.Config, hc); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the isolation level
|
// Validate the isolation level
|
||||||
if err := ValidateIsolationLevel(hc); err != nil {
|
if err := ValidateIsolationLevel(hc); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
return w.Config, hc, nil
|
return w.Config, hc, w.NetworkingConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateVolumesAndBindSettings validates each of the volumes and bind settings
|
// validateVolumesAndBindSettings validates each of the volumes and bind settings
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/docker/engine-api/types/strslice"
|
"github.com/docker/engine-api/types/strslice"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ func TestDecodeContainerConfig(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, h, err := DecodeContainerConfig(bytes.NewReader(b))
|
c, h, _, err := DecodeContainerConfig(bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
||||||
}
|
}
|
||||||
|
@ -70,29 +71,29 @@ func TestDecodeContainerConfig(t *testing.T) {
|
||||||
func TestDecodeContainerConfigIsolation(t *testing.T) {
|
func TestDecodeContainerConfigIsolation(t *testing.T) {
|
||||||
|
|
||||||
// An invalid isolation level
|
// An invalid isolation level
|
||||||
if _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
if _, _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
||||||
if !strings.Contains(err.Error(), `invalid --isolation: "invalid"`) {
|
if !strings.Contains(err.Error(), `invalid --isolation: "invalid"`) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blank isolation level (== default)
|
// Blank isolation level (== default)
|
||||||
if _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
if _, _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
||||||
t.Fatal("Blank isolation should have succeeded")
|
t.Fatal("Blank isolation should have succeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default isolation level
|
// Default isolation level
|
||||||
if _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
if _, _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
||||||
t.Fatal("default isolation should have succeeded")
|
t.Fatal("default isolation should have succeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hyper-V Containers isolation level (Valid on Windows only)
|
// Hyper-V Containers isolation level (Valid on Windows only)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
||||||
t.Fatal("hyperv isolation should have succeeded")
|
t.Fatal("hyperv isolation should have succeeded")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
||||||
if !strings.Contains(err.Error(), `invalid --isolation: "hyperv"`) {
|
if !strings.Contains(err.Error(), `invalid --isolation: "hyperv"`) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -102,7 +103,7 @@ func TestDecodeContainerConfigIsolation(t *testing.T) {
|
||||||
|
|
||||||
// callDecodeContainerConfigIsolation is a utility function to call
|
// callDecodeContainerConfigIsolation is a utility function to call
|
||||||
// DecodeContainerConfig for validating isolation levels
|
// DecodeContainerConfig for validating isolation levels
|
||||||
func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *container.HostConfig, error) {
|
func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) {
|
||||||
var (
|
var (
|
||||||
b []byte
|
b []byte
|
||||||
err error
|
err error
|
||||||
|
@ -114,7 +115,7 @@ func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *c
|
||||||
Isolation: container.IsolationLevel(isolation)},
|
Isolation: container.IsolationLevel(isolation)},
|
||||||
}
|
}
|
||||||
if b, err = json.Marshal(w); err != nil {
|
if b, err = json.Marshal(w); err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
return nil, nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
||||||
}
|
}
|
||||||
return DecodeContainerConfig(bytes.NewReader(b))
|
return DecodeContainerConfig(bytes.NewReader(b))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,19 @@
|
||||||
|
|
||||||
package runconfig
|
package runconfig
|
||||||
|
|
||||||
import "github.com/docker/engine-api/types/container"
|
import (
|
||||||
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
|
)
|
||||||
|
|
||||||
// ContainerConfigWrapper is a Config wrapper that hold the container Config (portable)
|
// ContainerConfigWrapper is a Config wrapper that hold the container Config (portable)
|
||||||
// and the corresponding HostConfig (non-portable).
|
// and the corresponding HostConfig (non-portable).
|
||||||
type ContainerConfigWrapper struct {
|
type ContainerConfigWrapper struct {
|
||||||
*container.Config
|
*container.Config
|
||||||
InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
||||||
Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility.
|
Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility.
|
||||||
*container.HostConfig // Deprecated. Exported to read attributes from json that are not in the inner host config structure.
|
NetworkingConfig *networktypes.NetworkingConfig `json:"NetworkingConfig,omitempty"`
|
||||||
|
*container.HostConfig // Deprecated. Exported to read attributes from json that are not in the inner host config structure.
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHostConfig gets the HostConfig of the Config.
|
// getHostConfig gets the HostConfig of the Config.
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package runconfig
|
package runconfig
|
||||||
|
|
||||||
import "github.com/docker/engine-api/types/container"
|
import (
|
||||||
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
|
)
|
||||||
|
|
||||||
// ContainerConfigWrapper is a Config wrapper that hold the container Config (portable)
|
// ContainerConfigWrapper is a Config wrapper that hold the container Config (portable)
|
||||||
// and the corresponding HostConfig (non-portable).
|
// and the corresponding HostConfig (non-portable).
|
||||||
type ContainerConfigWrapper struct {
|
type ContainerConfigWrapper struct {
|
||||||
*container.Config
|
*container.Config
|
||||||
HostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
HostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
||||||
|
NetworkingConfig *networktypes.NetworkingConfig `json:"NetworkingConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHostConfig gets the HostConfig of the Config.
|
// getHostConfig gets the HostConfig of the Config.
|
||||||
|
|
|
@ -29,4 +29,8 @@ var (
|
||||||
ErrConflictNetworkPublishPorts = fmt.Errorf("Conflicting options: port publishing and the container type network mode")
|
ErrConflictNetworkPublishPorts = fmt.Errorf("Conflicting options: port publishing and the container type network mode")
|
||||||
// ErrConflictNetworkExposePorts conflict between the expose option and the network mode
|
// ErrConflictNetworkExposePorts conflict between the expose option and the network mode
|
||||||
ErrConflictNetworkExposePorts = fmt.Errorf("Conflicting options: port exposing and the container type network mode")
|
ErrConflictNetworkExposePorts = fmt.Errorf("Conflicting options: port exposing and the container type network mode")
|
||||||
|
// ErrUnsupportedNetworkAndIP conflict between network mode and preferred ip address
|
||||||
|
ErrUnsupportedNetworkAndIP = fmt.Errorf("User specified IP address is supported on user defined networks only")
|
||||||
|
// ErrUnsupportedNetworkNoSubnetAndIP conflict between network with no configured subnet and preferred ip address
|
||||||
|
ErrUnsupportedNetworkNoSubnetAndIP = fmt.Errorf("User specified IP address is supported only when connecting to networks with user configured subnets")
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/docker/engine-api/types/strslice"
|
"github.com/docker/engine-api/types/strslice"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
|
@ -19,7 +20,7 @@ import (
|
||||||
// Parse parses the specified args for the specified command and generates a Config,
|
// Parse parses the specified args for the specified command and generates a Config,
|
||||||
// a HostConfig and returns them with the specified command.
|
// a HostConfig and returns them with the specified command.
|
||||||
// If the specified args are not valid, it will return an error.
|
// If the specified args are not valid, it will return an error.
|
||||||
func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *flag.FlagSet, error) {
|
func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
||||||
var (
|
var (
|
||||||
// FIXME: use utils.ListOpts for attach and volumes?
|
// FIXME: use utils.ListOpts for attach and volumes?
|
||||||
flAttach = opts.NewListOpts(ValidateAttach)
|
flAttach = opts.NewListOpts(ValidateAttach)
|
||||||
|
@ -77,6 +78,8 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)")
|
flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tune container memory swappiness (0 to 100)")
|
||||||
flNetMode = cmd.String([]string{"-net"}, "default", "Connect a container to a network")
|
flNetMode = cmd.String([]string{"-net"}, "default", "Connect a container to a network")
|
||||||
flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
|
flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)")
|
||||||
|
flIPv4Address = cmd.String([]string{"-ip"}, "", "Container IPv4 address (e.g. 172.30.100.104)")
|
||||||
|
flIPv6Address = cmd.String([]string{"-ip6"}, "", "Container IPv6 address (e.g. 2001:db8::33)")
|
||||||
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use")
|
||||||
flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits")
|
||||||
flReadonlyRootfs = cmd.Bool([]string{"-read-only"}, false, "Mount the container's root filesystem as read only")
|
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
|
||||||
cmd.Require(flag.Min, 1)
|
cmd.Require(flag.Min, 1)
|
||||||
|
|
||||||
if err := cmd.ParseFlags(args, true); err != nil {
|
if err := cmd.ParseFlags(args, true); err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -131,7 +134,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
// Validate the input mac address
|
// Validate the input mac address
|
||||||
if *flMacAddress != "" {
|
if *flMacAddress != "" {
|
||||||
if _, err := ValidateMACAddress(*flMacAddress); err != nil {
|
if _, err := ValidateMACAddress(*flMacAddress); err != nil {
|
||||||
return nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
return nil, nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *flStdin {
|
if *flStdin {
|
||||||
|
@ -149,7 +152,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
if *flMemoryString != "" {
|
if *flMemoryString != "" {
|
||||||
flMemory, err = units.RAMInBytes(*flMemoryString)
|
flMemory, err = units.RAMInBytes(*flMemoryString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
if *flMemoryReservation != "" {
|
if *flMemoryReservation != "" {
|
||||||
MemoryReservation, err = units.RAMInBytes(*flMemoryReservation)
|
MemoryReservation, err = units.RAMInBytes(*flMemoryReservation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +171,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
} else {
|
} else {
|
||||||
memorySwap, err = units.RAMInBytes(*flMemorySwap)
|
memorySwap, err = units.RAMInBytes(*flMemorySwap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,20 +180,20 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
if *flKernelMemory != "" {
|
if *flKernelMemory != "" {
|
||||||
KernelMemory, err = units.RAMInBytes(*flKernelMemory)
|
KernelMemory, err = units.RAMInBytes(*flKernelMemory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
swappiness := *flSwappiness
|
swappiness := *flSwappiness
|
||||||
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
if swappiness != -1 && (swappiness < 0 || swappiness > 100) {
|
||||||
return nil, nil, cmd, fmt.Errorf("Invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
return nil, nil, nil, cmd, fmt.Errorf("Invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
|
||||||
}
|
}
|
||||||
|
|
||||||
var shmSize int64
|
var shmSize int64
|
||||||
if *flShmSize != "" {
|
if *flShmSize != "" {
|
||||||
shmSize, err = units.RAMInBytes(*flShmSize)
|
shmSize, err = units.RAMInBytes(*flShmSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,7 +213,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
for _, t := range flTmpfs.GetAll() {
|
for _, t := range flTmpfs.GetAll() {
|
||||||
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
||||||
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
tmpfs[arr[0]] = arr[1]
|
tmpfs[arr[0]] = arr[1]
|
||||||
} else {
|
} else {
|
||||||
|
@ -243,13 +246,13 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
|
|
||||||
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
|
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge in exposed ports to the map of published ports
|
// Merge in exposed ports to the map of published ports
|
||||||
for _, e := range flExpose.GetAll() {
|
for _, e := range flExpose.GetAll() {
|
||||||
if strings.Contains(e, ":") {
|
if strings.Contains(e, ":") {
|
||||||
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
|
return nil, nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
|
||||||
}
|
}
|
||||||
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
||||||
proto, port := nat.SplitProtoPort(e)
|
proto, port := nat.SplitProtoPort(e)
|
||||||
|
@ -257,12 +260,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
//if expose a port, the start and end port are the same
|
//if expose a port, the start and end port are the same
|
||||||
start, end, err := nat.ParsePortRange(port)
|
start, end, err := nat.ParsePortRange(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, fmt.Errorf("Invalid range format for --expose: %s, error: %s", e, err)
|
return nil, nil, nil, cmd, fmt.Errorf("Invalid range format for --expose: %s, error: %s", e, err)
|
||||||
}
|
}
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
if _, exists := ports[p]; !exists {
|
if _, exists := ports[p]; !exists {
|
||||||
ports[p] = struct{}{}
|
ports[p] = struct{}{}
|
||||||
|
@ -275,7 +278,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
for _, device := range flDevices.GetAll() {
|
for _, device := range flDevices.GetAll() {
|
||||||
deviceMapping, err := ParseDevice(device)
|
deviceMapping, err := ParseDevice(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
deviceMappings = append(deviceMappings, deviceMapping)
|
deviceMappings = append(deviceMappings, deviceMapping)
|
||||||
}
|
}
|
||||||
|
@ -283,38 +286,38 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
// collect all the environment variables for the container
|
// collect all the environment variables for the container
|
||||||
envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll())
|
envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the labels for the container
|
// collect all the labels for the container
|
||||||
labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll())
|
labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMode := container.IpcMode(*flIpcMode)
|
ipcMode := container.IpcMode(*flIpcMode)
|
||||||
if !ipcMode.Valid() {
|
if !ipcMode.Valid() {
|
||||||
return nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
|
return nil, nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
pidMode := container.PidMode(*flPidMode)
|
pidMode := container.PidMode(*flPidMode)
|
||||||
if !pidMode.Valid() {
|
if !pidMode.Valid() {
|
||||||
return nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
|
return nil, nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
utsMode := container.UTSMode(*flUTSMode)
|
utsMode := container.UTSMode(*flUTSMode)
|
||||||
if !utsMode.Valid() {
|
if !utsMode.Valid() {
|
||||||
return nil, nil, cmd, fmt.Errorf("--uts: invalid UTS mode")
|
return nil, nil, nil, cmd, fmt.Errorf("--uts: invalid UTS mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
restartPolicy, err := ParseRestartPolicy(*flRestartPolicy)
|
restartPolicy, err := ParseRestartPolicy(*flRestartPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll())
|
loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, cmd, err
|
return nil, nil, nil, cmd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resources := container.Resources{
|
resources := container.Resources{
|
||||||
|
@ -405,7 +408,21 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
||||||
if config.OpenStdin && config.AttachStdin {
|
if config.OpenStdin && config.AttachStdin {
|
||||||
config.StdinOnce = true
|
config.StdinOnce = true
|
||||||
}
|
}
|
||||||
return config, hostConfig, cmd, nil
|
|
||||||
|
var networkingConfig *networktypes.NetworkingConfig
|
||||||
|
if *flIPv4Address != "" || *flIPv6Address != "" {
|
||||||
|
networkingConfig = &networktypes.NetworkingConfig{
|
||||||
|
EndpointsConfig: make(map[string]*networktypes.EndpointSettings),
|
||||||
|
}
|
||||||
|
networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)] = &networktypes.EndpointSettings{
|
||||||
|
IPAMConfig: &networktypes.EndpointIPAMConfig{
|
||||||
|
IPv4Address: *flIPv4Address,
|
||||||
|
IPv6Address: *flIPv6Address,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, hostConfig, networkingConfig, cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reads a file of line terminated key=value pairs and override that with override parameter
|
// reads a file of line terminated key=value pairs and override that with override parameter
|
||||||
|
|
|
@ -13,10 +13,11 @@ import (
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/engine-api/types/container"
|
"github.com/docker/engine-api/types/container"
|
||||||
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseRun(args []string) (*container.Config, *container.HostConfig, *flag.FlagSet, error) {
|
func parseRun(args []string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, *flag.FlagSet, error) {
|
||||||
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
||||||
cmd.SetOutput(ioutil.Discard)
|
cmd.SetOutput(ioutil.Discard)
|
||||||
cmd.Usage = nil
|
cmd.Usage = nil
|
||||||
|
@ -24,7 +25,7 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *flag.Fl
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(t *testing.T, args string) (*container.Config, *container.HostConfig, error) {
|
func parse(t *testing.T, args string) (*container.Config, *container.HostConfig, error) {
|
||||||
config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
config, hostConfig, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||||
return config, hostConfig, err
|
return config, hostConfig, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +305,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con
|
||||||
if b, err = json.Marshal(w); err != nil {
|
if b, err = json.Marshal(w); err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
return nil, nil, fmt.Errorf("Error on marshal %s", err.Error())
|
||||||
}
|
}
|
||||||
c, h, err = runconfig.DecodeContainerConfig(bytes.NewReader(b))
|
c, h, _, err = runconfig.DecodeContainerConfig(bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error parsing %s: %v", string(b), err)
|
return nil, nil, fmt.Errorf("Error parsing %s: %v", string(b), err)
|
||||||
}
|
}
|
||||||
|
@ -349,7 +350,7 @@ func setupPlatformVolume(u []string, w []string) ([]string, string) {
|
||||||
func TestParseWithMacAddress(t *testing.T) {
|
func TestParseWithMacAddress(t *testing.T) {
|
||||||
invalidMacAddress := "--mac-address=invalidMacAddress"
|
invalidMacAddress := "--mac-address=invalidMacAddress"
|
||||||
validMacAddress := "--mac-address=92:d0:c6:0a:29:33"
|
validMacAddress := "--mac-address=92:d0:c6:0a:29:33"
|
||||||
if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
if _, _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
|
||||||
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
|
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
|
||||||
}
|
}
|
||||||
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
|
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
|
||||||
|
@ -360,7 +361,7 @@ func TestParseWithMacAddress(t *testing.T) {
|
||||||
func TestParseWithMemory(t *testing.T) {
|
func TestParseWithMemory(t *testing.T) {
|
||||||
invalidMemory := "--memory=invalid"
|
invalidMemory := "--memory=invalid"
|
||||||
validMemory := "--memory=1G"
|
validMemory := "--memory=1G"
|
||||||
if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err != nil && err.Error() != "invalid size: 'invalid'" {
|
||||||
t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
|
t.Fatalf("Expected an error with '%v' Memory, got '%v'", invalidMemory, err)
|
||||||
}
|
}
|
||||||
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
||||||
|
@ -372,7 +373,7 @@ func TestParseWithMemorySwap(t *testing.T) {
|
||||||
invalidMemory := "--memory-swap=invalid"
|
invalidMemory := "--memory-swap=invalid"
|
||||||
validMemory := "--memory-swap=1G"
|
validMemory := "--memory-swap=1G"
|
||||||
anotherValidMemory := "--memory-swap=-1"
|
anotherValidMemory := "--memory-swap=-1"
|
||||||
if _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
if _, _, _, _, err := parseRun([]string{invalidMemory, "img", "cmd"}); err == nil || err.Error() != "invalid size: 'invalid'" {
|
||||||
t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
|
t.Fatalf("Expected an error with '%v' MemorySwap, got '%v'", invalidMemory, err)
|
||||||
}
|
}
|
||||||
if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
|
if _, hostconfig := mustParse(t, validMemory); hostconfig.MemorySwap != 1073741824 {
|
||||||
|
@ -417,12 +418,12 @@ func TestParseWithExpose(t *testing.T) {
|
||||||
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
|
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
|
||||||
}
|
}
|
||||||
for expose, expectedError := range invalids {
|
for expose, expectedError := range invalids {
|
||||||
if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
||||||
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
|
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for expose, exposedPorts := range valids {
|
for expose, exposedPorts := range valids {
|
||||||
config, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
config, _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -436,7 +437,7 @@ func TestParseWithExpose(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Merge with actual published port
|
// Merge with actual published port
|
||||||
config, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
config, _, _, _, err := parseRun([]string{"--publish=80", "--expose=80-81/tcp", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -475,7 +476,7 @@ func TestParseDevice(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for device, deviceMapping := range valids {
|
for device, deviceMapping := range valids {
|
||||||
_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
_, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--device=%v", device), "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -491,11 +492,11 @@ func TestParseDevice(t *testing.T) {
|
||||||
|
|
||||||
func TestParseModes(t *testing.T) {
|
func TestParseModes(t *testing.T) {
|
||||||
// ipc ko
|
// ipc ko
|
||||||
if _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
if _, _, _, _, err := parseRun([]string{"--ipc=container:", "img", "cmd"}); err == nil || err.Error() != "--ipc: invalid IPC mode" {
|
||||||
t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
|
t.Fatalf("Expected an error with message '--ipc: invalid IPC mode', got %v", err)
|
||||||
}
|
}
|
||||||
// ipc ok
|
// ipc ok
|
||||||
_, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
_, hostconfig, _, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -503,11 +504,11 @@ func TestParseModes(t *testing.T) {
|
||||||
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
||||||
}
|
}
|
||||||
// pid ko
|
// pid ko
|
||||||
if _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
if _, _, _, _, err := parseRun([]string{"--pid=container:", "img", "cmd"}); err == nil || err.Error() != "--pid: invalid PID mode" {
|
||||||
t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
|
t.Fatalf("Expected an error with message '--pid: invalid PID mode', got %v", err)
|
||||||
}
|
}
|
||||||
// pid ok
|
// pid ok
|
||||||
_, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
_, hostconfig, _, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -515,11 +516,11 @@ func TestParseModes(t *testing.T) {
|
||||||
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
||||||
}
|
}
|
||||||
// uts ko
|
// uts ko
|
||||||
if _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
if _, _, _, _, err := parseRun([]string{"--uts=container:", "img", "cmd"}); err == nil || err.Error() != "--uts: invalid UTS mode" {
|
||||||
t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
|
t.Fatalf("Expected an error with message '--uts: invalid UTS mode', got %v", err)
|
||||||
}
|
}
|
||||||
// uts ok
|
// uts ok
|
||||||
_, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
_, hostconfig, _, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -527,11 +528,11 @@ func TestParseModes(t *testing.T) {
|
||||||
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
||||||
}
|
}
|
||||||
// shm-size ko
|
// shm-size ko
|
||||||
if _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
if _, _, _, _, err = parseRun([]string{"--shm-size=a128m", "img", "cmd"}); err == nil || err.Error() != "invalid size: 'a128m'" {
|
||||||
t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
|
t.Fatalf("Expected an error with message 'invalid size: a128m', got %v", err)
|
||||||
}
|
}
|
||||||
// shm-size ok
|
// shm-size ok
|
||||||
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
_, hostconfig, _, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -560,12 +561,12 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for restart, expectedError := range invalids {
|
for restart, expectedError := range invalids {
|
||||||
if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
if _, _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
|
||||||
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for restart, expected := range valids {
|
for restart, expected := range valids {
|
||||||
_, hostconfig, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
_, hostconfig, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%v", restart), "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -577,11 +578,11 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||||
|
|
||||||
func TestParseLoggingOpts(t *testing.T) {
|
func TestParseLoggingOpts(t *testing.T) {
|
||||||
// logging opts ko
|
// logging opts ko
|
||||||
if _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "Invalid logging opts for driver none" {
|
if _, _, _, _, err := parseRun([]string{"--log-driver=none", "--log-opt=anything", "img", "cmd"}); err == nil || err.Error() != "Invalid logging opts for driver none" {
|
||||||
t.Fatalf("Expected an error with message 'Invalid logging opts for driver none', got %v", err)
|
t.Fatalf("Expected an error with message 'Invalid logging opts for driver none', got %v", err)
|
||||||
}
|
}
|
||||||
// logging opts ok
|
// logging opts ok
|
||||||
_, hostconfig, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
_, hostconfig, _, _, err := parseRun([]string{"--log-driver=syslog", "--log-opt=something", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -596,18 +597,18 @@ func TestParseEnvfileVariables(t *testing.T) {
|
||||||
e = "open nonexistent: The system cannot find the file specified."
|
e = "open nonexistent: The system cannot find the file specified."
|
||||||
}
|
}
|
||||||
// env ko
|
// env ko
|
||||||
if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
if _, _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
||||||
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
||||||
}
|
}
|
||||||
// env ok
|
// env ok
|
||||||
config, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
config, _, _, _, err := parseRun([]string{"--env-file=fixtures/valid.env", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
||||||
t.Fatalf("Expected a a config with [ENV1=value1], got %v", config.Env)
|
t.Fatalf("Expected a a config with [ENV1=value1], got %v", config.Env)
|
||||||
}
|
}
|
||||||
config, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
config, _, _, _, err = parseRun([]string{"--env-file=fixtures/valid.env", "--env=ENV2=value2", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -622,18 +623,18 @@ func TestParseLabelfileVariables(t *testing.T) {
|
||||||
e = "open nonexistent: The system cannot find the file specified."
|
e = "open nonexistent: The system cannot find the file specified."
|
||||||
}
|
}
|
||||||
// label ko
|
// label ko
|
||||||
if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
if _, _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
||||||
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
||||||
}
|
}
|
||||||
// label ok
|
// label ok
|
||||||
config, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
config, _, _, _, err := parseRun([]string{"--label-file=fixtures/valid.label", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
||||||
t.Fatalf("Expected a a config with [LABEL1:value1], got %v", config.Labels)
|
t.Fatalf("Expected a a config with [LABEL1:value1], got %v", config.Labels)
|
||||||
}
|
}
|
||||||
config, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
config, _, _, _, err = parseRun([]string{"--label-file=fixtures/valid.label", "--label=LABEL2=value2", "img", "cmd"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -643,7 +644,7 @@ func TestParseLabelfileVariables(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseEntryPoint(t *testing.T) {
|
func TestParseEntryPoint(t *testing.T) {
|
||||||
config, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
config, _, _, _, err := parseRun([]string{"--entrypoint=anything", "cmd", "img"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue