зеркало из https://github.com/microsoft/docker.git
Allow user to choose the IP address for the container
Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Родитель
19b063e740
Коммит
2bb3fc1bc5
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/docker/engine-api/client"
|
||||
"github.com/docker/engine-api/types"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
)
|
||||
|
||||
func (cli *DockerCli) pullImage(image string) error {
|
||||
|
@ -79,7 +80,7 @@ func newCIDFile(path string) (*cidFile, error) {
|
|||
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
|
||||
if cidfile != "" {
|
||||
var err error
|
||||
|
@ -107,7 +108,8 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
|
|||
}
|
||||
|
||||
//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 err != nil {
|
||||
if client.IsErrImageNotFound(err) {
|
||||
|
@ -124,7 +126,7 @@ func (cli *DockerCli) createContainer(config *container.Config, hostConfig *cont
|
|||
}
|
||||
// Retry
|
||||
var retryErr error
|
||||
response, retryErr = cli.client.ContainerCreate(config, hostConfig, nil, name)
|
||||
response, retryErr = cli.client.ContainerCreate(config, hostConfig, networkingConfig, name)
|
||||
if retryErr != nil {
|
||||
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")
|
||||
)
|
||||
|
||||
config, hostConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||
config, hostConfig, networkingConfig, cmd, err := runconfigopts.Parse(cmd, args)
|
||||
|
||||
if err != nil {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
os.Exit(1)
|
||||
|
@ -165,7 +168,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
|
|||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
response, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
||||
response, err := cli.createContainer(config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -107,15 +107,22 @@ func (cli *DockerCli) CmdNetworkRm(args ...string) error {
|
|||
|
||||
// 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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1), nil)
|
||||
epConfig := &network.EndpointSettings{
|
||||
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
|
||||
|
|
|
@ -82,7 +82,8 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
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
|
||||
if err != nil {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
|
@ -145,7 +146,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
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 {
|
||||
cmd.ReportError(err.Error(), true)
|
||||
return runStartContainerErr(err)
|
||||
|
|
|
@ -332,7 +332,7 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon
|
|||
return err
|
||||
}
|
||||
|
||||
_, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
||||
_, hostConfig, _, err := runconfig.DecodeContainerConfig(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
|||
|
||||
name := r.Form.Get("name")
|
||||
|
||||
config, hostConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
||||
config, hostConfig, networkingConfig, err := runconfig.DecodeContainerConfig(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -366,10 +366,11 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
|
|||
adjustCPUShares := version.LessThan("1.19")
|
||||
|
||||
ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{
|
||||
Name: name,
|
||||
Config: config,
|
||||
HostConfig: hostConfig,
|
||||
AdjustCPUShares: adjustCPUShares,
|
||||
Name: name,
|
||||
Config: config,
|
||||
HostConfig: hostConfig,
|
||||
NetworkingConfig: networkingConfig,
|
||||
AdjustCPUShares: adjustCPUShares,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -39,7 +39,7 @@ func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.
|
|||
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.
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package network
|
|||
|
||||
import (
|
||||
"github.com/docker/engine-api/types/network"
|
||||
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
|
@ -15,7 +14,7 @@ type Backend interface {
|
|||
GetAllNetworks() []libnetwork.Network
|
||||
CreateNetwork(name, driver string, ipam network.IPAM,
|
||||
options map[string]string) (libnetwork.Network, error)
|
||||
ConnectContainerToNetwork(containerName, networkName string) error
|
||||
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
|
||||
DisconnectContainerFromNetwork(containerName string,
|
||||
network libnetwork.Network) error
|
||||
NetworkControllerEnabled() bool
|
||||
|
|
|
@ -122,7 +122,7 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
|
|||
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 {
|
||||
|
|
|
@ -261,6 +261,14 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network) ([]
|
|||
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
|
||||
// to which container was connected to on docker run.
|
||||
if n.Name() != container.HostConfig.NetworkMode.NetworkName() &&
|
||||
|
|
|
@ -503,7 +503,10 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
|
|||
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
|
||||
}
|
||||
|
@ -562,7 +565,12 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
|||
}
|
||||
|
||||
// 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
|
||||
if container.Config.NetworkDisabled || mode.IsContainer() {
|
||||
return nil
|
||||
|
@ -573,14 +581,35 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
|
|||
networkName = daemon.netController.Config().Daemon.DefaultNetwork
|
||||
}
|
||||
if mode.IsUserDefined() {
|
||||
n, err := daemon.FindNetwork(networkName)
|
||||
n, err = daemon.FindNetwork(networkName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkName = n.Name()
|
||||
}
|
||||
container.NetworkSettings.Networks = make(map[string]*networktypes.EndpointSettings)
|
||||
container.NetworkSettings.Networks[networkName] = new(networktypes.EndpointSettings)
|
||||
if container.NetworkSettings == nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -598,15 +627,15 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
err := daemon.updateContainerNetworkSettings(container)
|
||||
err := daemon.updateContainerNetworkSettings(container, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateSettings = true
|
||||
}
|
||||
|
||||
for n := range container.NetworkSettings.Networks {
|
||||
if err := daemon.connectToNetwork(container, n, updateSettings); err != nil {
|
||||
for n, nConf := range container.NetworkSettings.Networks {
|
||||
if err := daemon.connectToNetwork(container, n, nConf, updateSettings); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -626,12 +655,65 @@ func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwo
|
|||
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
|
||||
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 {
|
||||
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
|
||||
}
|
||||
if err := container.ToDiskLocking(); err != nil {
|
||||
|
@ -640,11 +722,15 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
|
|||
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() {
|
||||
return runconfig.ErrConflictSharedNetwork
|
||||
}
|
||||
|
||||
if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) {
|
||||
return runconfig.ErrUnsupportedNetworkAndIP
|
||||
}
|
||||
|
||||
if containertypes.NetworkMode(idOrName).IsBridge() &&
|
||||
daemon.configStore.DisableBridge {
|
||||
container.Config.NetworkDisabled = true
|
||||
|
@ -658,12 +744,20 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
|
|||
return err
|
||||
}
|
||||
|
||||
if err := validateNetworkingConfig(n, endpointConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if updateSettings {
|
||||
if err := daemon.updateNetworkSettings(container, n); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if endpointConfig != nil {
|
||||
container.NetworkSettings.Networks[n.Name()] = endpointConfig
|
||||
}
|
||||
|
||||
ep, err := container.GetEndpointInNetwork(n)
|
||||
if err == nil {
|
||||
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
|
||||
settings := container.NetworkSettings.Networks
|
||||
if sid == "" || len(settings) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var networks []libnetwork.Network
|
||||
for n := range settings {
|
||||
for n, epSettings := range settings {
|
||||
if nw, err := daemon.FindNetwork(n); err == nil {
|
||||
networks = append(networks, nw)
|
||||
}
|
||||
settings[n] = &networktypes.EndpointSettings{}
|
||||
}
|
||||
|
||||
container.NetworkSettings = &network.Settings{Networks: settings}
|
||||
|
||||
if sid == "" || len(settings) == 0 {
|
||||
return
|
||||
cleanOperationalData(epSettings)
|
||||
}
|
||||
|
||||
sb, err := daemon.netController.SandboxByID(sid)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/docker/docker/daemon/execdriver/windows"
|
||||
derr "github.com/docker/docker/errors"
|
||||
"github.com/docker/docker/layer"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/libnetwork"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -27,7 +28,7 @@ func (daemon *Daemon) initializeNetworking(container *container.Container) error
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
volumestore "github.com/docker/docker/volume/store"
|
||||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
)
|
||||
|
||||
|
@ -108,7 +109,12 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (retC *containe
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -150,12 +150,12 @@ func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnet
|
|||
// ConnectContainerToNetwork connects the given container to the given
|
||||
// network. If either cannot be found, an err is returned. If the
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return daemon.ConnectToNetwork(container, networkName)
|
||||
return daemon.ConnectToNetwork(container, networkName, endpointConfig)
|
||||
}
|
||||
|
||||
// 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
|
||||
device (in bytes per second or IO per second).
|
||||
* `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
|
||||
|
||||
|
|
|
@ -3031,7 +3031,13 @@ POST /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30/
|
|||
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
|
||||
```
|
||||
|
||||
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.
|
||||
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
|
||||
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.
|
||||
|
||||
|
|
|
@ -56,6 +56,8 @@ parent = "smn_cli"
|
|||
--log-opt=[] Log driver specific options
|
||||
-m, --memory="" Memory limit
|
||||
--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-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.
|
||||
|
|
|
@ -275,6 +275,8 @@ of the containers.
|
|||
'<network-name>|<network-id>': connect to a user-defined network
|
||||
--add-host="" : Add a line to /etc/hosts (host:IP)
|
||||
--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
|
||||
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.
|
||||
|
||||
```bash
|
||||
$ docker network create -d bridge isolated_nw
|
||||
f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a
|
||||
$ docker network create -d bridge --subnet 172.25.0.0/16 isolated_nw
|
||||
06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8
|
||||
```
|
||||
|
||||
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 inspect isolated_nw
|
||||
[[
|
||||
[
|
||||
{
|
||||
"Name": "isolated_nw",
|
||||
"Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a",
|
||||
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
|
||||
"Scope": "local",
|
||||
"Driver": "bridge",
|
||||
"IPAM": {
|
||||
"Driver": "default",
|
||||
"Config": [
|
||||
{}
|
||||
{
|
||||
"Subnet": "172.25.0.0/16"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Containers": {
|
||||
"498eaaaf328e1018042c04b2de04036fc04719a6e39a097a4f4866043a2c2152": {
|
||||
"EndpointID": "0e24479cfaafb029104999b4e120858a07b19b1b6d956ae56811033e45d68ad9",
|
||||
"MacAddress": "02:42:ac:15:00:02",
|
||||
"IPv4Address": "172.21.0.2/16",
|
||||
"90e1f3ec71caf82ae776a827e0712a68a110a3f175954e5bd4222fd142ac9428": {
|
||||
"Name": "container2",
|
||||
"EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
|
||||
"MacAddress": "02:42:ac:19:00:02",
|
||||
"IPv4Address": "172.25.0.2/16",
|
||||
"IPv6Address": ""
|
||||
}
|
||||
},
|
||||
|
@ -150,20 +153,28 @@ $ docker network inspect isolated_nw
|
|||
```
|
||||
|
||||
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
|
||||
would have used that addressing. Now, start a third container and connect it to
|
||||
Given we specified a `--subnet` when creating the network, Engine picked
|
||||
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:
|
||||
|
||||
```bash
|
||||
$ docker run --net=isolated_nw -itd --name=container3 busybox
|
||||
c282ca437ee7e926a7303a64fc04109740208d2c20e442366139322211a6481c
|
||||
$ docker run --net=isolated_nw --ip=172.25.3.3 -itd --name=container3 busybox
|
||||
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`.
|
||||
|
||||
```bash
|
||||
$ 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.
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
"bridge": {
|
||||
"EndpointID": "281b5ead415cf48a6a84fd1a6504342c76e9091fe09b4fdbcc4a01c30b0d3c5b",
|
||||
"EndpointID": "0099f9efb5a3727f6a554f176b1e96fca34cae773da68b3b6a26d046c12cb365",
|
||||
"Gateway": "172.17.0.1",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"IPAMConfig": null,
|
||||
"IPAddress": "172.17.0.3",
|
||||
"IPPrefixLen": 16,
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": "02:42:ac:11:00:03"
|
||||
},
|
||||
"isolated_nw": {
|
||||
"EndpointID": "0e24479cfaafb029104999b4e120858a07b19b1b6d956ae56811033e45d68ad9",
|
||||
"Gateway": "172.21.0.1",
|
||||
"EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
|
||||
"Gateway": "172.25.0.1",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"IPAddress": "172.21.0.2",
|
||||
"IPAMConfig": null,
|
||||
"IPAddress": "172.25.0.2",
|
||||
"IPPrefixLen": 16,
|
||||
"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)
|
||||
|
||||
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
|
||||
inet6 addr: fe80::42:acff:fe15:2/64 Scope:Link
|
||||
inet addr:172.25.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
|
||||
inet6 addr: fe80::42:acff:fe19:2/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
|
||||
|
@ -252,19 +265,19 @@ fe00::0 ip6-localnet
|
|||
ff00::0 ip6-mcastprefix
|
||||
ff02::1 ip6-allnodes
|
||||
ff02::2 ip6-allrouters
|
||||
172.21.0.3 container3
|
||||
172.21.0.3 container3.isolated_nw
|
||||
172.21.3.3 container3
|
||||
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.
|
||||
|
||||
```bash
|
||||
/ # ping -w 4 container3
|
||||
PING container3 (172.21.0.3): 56 data bytes
|
||||
64 bytes from 172.21.0.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.21.0.3: seq=2 ttl=64 time=0.080 ms
|
||||
64 bytes from 172.21.0.3: seq=3 ttl=64 time=0.097 ms
|
||||
PING container3 (172.25.3.3): 56 data bytes
|
||||
64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.070 ms
|
||||
64 bytes from 172.25.3.3: seq=1 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.25.3.3: seq=3 ttl=64 time=0.097 ms
|
||||
|
||||
--- container3 ping statistics ---
|
||||
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
|
||||
[[
|
||||
[
|
||||
{
|
||||
"Name": "isolated_nw",
|
||||
"Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a",
|
||||
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
|
||||
"Scope": "local",
|
||||
"Driver": "bridge",
|
||||
"IPAM": {
|
||||
"Driver": "default",
|
||||
"Config": [
|
||||
{}
|
||||
{
|
||||
"Subnet": "172.25.0.0/16"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Containers": {
|
||||
"c282ca437ee7e926a7303a64fc04109740208d2c20e442366139322211a6481c": {
|
||||
"EndpointID": "e5d077f9712a69c6929fdd890df5e7c1c649771a50df5b422f7e68f0ae61e847",
|
||||
"MacAddress": "02:42:ac:15:00:03",
|
||||
"IPv4Address": "172.21.0.3/16",
|
||||
"467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551": {
|
||||
"Name": "container3",
|
||||
"EndpointID": "dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103",
|
||||
"MacAddress": "02:42:ac:19:03:03",
|
||||
"IPv4Address": "172.25.3.3/16",
|
||||
"IPv6Address": ""
|
||||
}
|
||||
},
|
||||
|
@ -393,7 +409,7 @@ lo Link encap:Local Loopback
|
|||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
||||
|
||||
/ # ping container3
|
||||
PING container3 (172.20.0.1): 56 data bytes
|
||||
PING container3 (172.25.3.3): 56 data bytes
|
||||
^C
|
||||
--- container3 ping statistics ---
|
||||
2 packets transmitted, 0 packets received, 100% packet loss
|
||||
|
@ -426,13 +442,15 @@ docker network inspect isolated_nw
|
|||
[
|
||||
{
|
||||
"Name": "isolated_nw",
|
||||
"Id": "f836c8deb6282ee614eade9d2f42d590e603d0b1efa0d99bd88b88c503e6ba7a",
|
||||
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
|
||||
"Scope": "local",
|
||||
"Driver": "bridge",
|
||||
"IPAM": {
|
||||
"Driver": "default",
|
||||
"Config": [
|
||||
{}
|
||||
{
|
||||
"Subnet": "172.25.0.0/16"
|
||||
}
|
||||
]
|
||||
},
|
||||
"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, "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/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
)
|
||||
|
||||
// DecodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper
|
||||
// struct and returns both a Config and an HostConfig struct
|
||||
// Be aware this function is not checking whether the resulted structs are nil,
|
||||
// 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
|
||||
|
||||
decoder := json.NewDecoder(src)
|
||||
if err := decoder.Decode(&w); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
hc := w.getHostConfig()
|
||||
|
@ -33,21 +34,21 @@ func DecodeContainerConfig(src io.Reader) (*container.Config, *container.HostCon
|
|||
|
||||
// Now validate all the volumes and binds
|
||||
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
|
||||
// on the client, as only the daemon knows what is valid for the platform.
|
||||
if err := ValidateNetMode(w.Config, hc); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Validate the isolation level
|
||||
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
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"github.com/docker/engine-api/types/strslice"
|
||||
)
|
||||
|
||||
|
@ -45,7 +46,7 @@ func TestDecodeContainerConfig(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c, h, err := DecodeContainerConfig(bytes.NewReader(b))
|
||||
c, h, _, err := DecodeContainerConfig(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err))
|
||||
}
|
||||
|
@ -70,29 +71,29 @@ func TestDecodeContainerConfig(t *testing.T) {
|
|||
func TestDecodeContainerConfigIsolation(t *testing.T) {
|
||||
|
||||
// An invalid isolation level
|
||||
if _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
||||
if _, _, _, err := callDecodeContainerConfigIsolation("invalid"); err != nil {
|
||||
if !strings.Contains(err.Error(), `invalid --isolation: "invalid"`) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Blank isolation level (== default)
|
||||
if _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
||||
if _, _, _, err := callDecodeContainerConfigIsolation(""); err != nil {
|
||||
t.Fatal("Blank isolation should have succeeded")
|
||||
}
|
||||
|
||||
// Default isolation level
|
||||
if _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
||||
if _, _, _, err := callDecodeContainerConfigIsolation("default"); err != nil {
|
||||
t.Fatal("default isolation should have succeeded")
|
||||
}
|
||||
|
||||
// Hyper-V Containers isolation level (Valid on Windows only)
|
||||
if runtime.GOOS == "windows" {
|
||||
if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
||||
if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
||||
t.Fatal("hyperv isolation should have succeeded")
|
||||
}
|
||||
} else {
|
||||
if _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
||||
if _, _, _, err := callDecodeContainerConfigIsolation("hyperv"); err != nil {
|
||||
if !strings.Contains(err.Error(), `invalid --isolation: "hyperv"`) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ func TestDecodeContainerConfigIsolation(t *testing.T) {
|
|||
|
||||
// callDecodeContainerConfigIsolation is a utility function to call
|
||||
// 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 (
|
||||
b []byte
|
||||
err error
|
||||
|
@ -114,7 +115,7 @@ func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *c
|
|||
Isolation: container.IsolationLevel(isolation)},
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -2,15 +2,19 @@
|
|||
|
||||
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)
|
||||
// and the corresponding HostConfig (non-portable).
|
||||
type ContainerConfigWrapper struct {
|
||||
*container.Config
|
||||
InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
||||
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.
|
||||
InnerHostConfig *container.HostConfig `json:"HostConfig,omitempty"`
|
||||
Cpuset string `json:",omitempty"` // Deprecated. Exported for backwards compatibility.
|
||||
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.
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
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)
|
||||
// and the corresponding HostConfig (non-portable).
|
||||
type ContainerConfigWrapper struct {
|
||||
*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.
|
||||
|
|
|
@ -29,4 +29,8 @@ var (
|
|||
ErrConflictNetworkPublishPorts = fmt.Errorf("Conflicting options: port publishing and the container type 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")
|
||||
// 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/signal"
|
||||
"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/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
|
@ -19,7 +20,7 @@ import (
|
|||
// Parse parses the specified args for the specified command and generates a Config,
|
||||
// a HostConfig and returns them with the specified command.
|
||||
// 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 (
|
||||
// FIXME: use utils.ListOpts for attach and volumes?
|
||||
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)")
|
||||
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)")
|
||||
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")
|
||||
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")
|
||||
|
@ -119,7 +122,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
cmd.Require(flag.Min, 1)
|
||||
|
||||
if err := cmd.ParseFlags(args, true); err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -131,7 +134,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
// Validate the input mac address
|
||||
if *flMacAddress != "" {
|
||||
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 {
|
||||
|
@ -149,7 +152,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
if *flMemoryString != "" {
|
||||
flMemory, err = units.RAMInBytes(*flMemoryString)
|
||||
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 != "" {
|
||||
MemoryReservation, err = units.RAMInBytes(*flMemoryReservation)
|
||||
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 {
|
||||
memorySwap, err = units.RAMInBytes(*flMemorySwap)
|
||||
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 != "" {
|
||||
KernelMemory, err = units.RAMInBytes(*flKernelMemory)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
}
|
||||
|
||||
swappiness := *flSwappiness
|
||||
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
|
||||
if *flShmSize != "" {
|
||||
shmSize, err = units.RAMInBytes(*flShmSize)
|
||||
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() {
|
||||
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
||||
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
tmpfs[arr[0]] = arr[1]
|
||||
} else {
|
||||
|
@ -243,13 +246,13 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
|
||||
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
|
||||
// Merge in exposed ports to the map of published ports
|
||||
for _, e := range flExpose.GetAll() {
|
||||
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>]
|
||||
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
|
||||
start, end, err := nat.ParsePortRange(port)
|
||||
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++ {
|
||||
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
if _, exists := ports[p]; !exists {
|
||||
ports[p] = struct{}{}
|
||||
|
@ -275,7 +278,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
for _, device := range flDevices.GetAll() {
|
||||
deviceMapping, err := ParseDevice(device)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
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
|
||||
envVariables, err := readKVStrings(flEnvFile.GetAll(), flEnv.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
|
||||
// collect all the labels for the container
|
||||
labels, err := readKVStrings(flLabelsFile.GetAll(), flLabels.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
|
||||
ipcMode := container.IpcMode(*flIpcMode)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
|
||||
loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll())
|
||||
if err != nil {
|
||||
return nil, nil, cmd, err
|
||||
return nil, nil, nil, cmd, err
|
||||
}
|
||||
|
||||
resources := container.Resources{
|
||||
|
@ -405,7 +408,21 @@ func Parse(cmd *flag.FlagSet, args []string) (*container.Config, *container.Host
|
|||
if config.OpenStdin && config.AttachStdin {
|
||||
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
|
||||
|
|
|
@ -13,10 +13,11 @@ import (
|
|||
flag "github.com/docker/docker/pkg/mflag"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
"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.SetOutput(ioutil.Discard)
|
||||
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) {
|
||||
config, hostConfig, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
config, hostConfig, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
return config, hostConfig, err
|
||||
}
|
||||
|
||||
|
@ -304,7 +305,7 @@ func callDecodeContainerConfig(volumes []string, binds []string) (*container.Con
|
|||
if b, err = json.Marshal(w); err != nil {
|
||||
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 {
|
||||
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) {
|
||||
invalidMacAddress := "--mac-address=invalidMacAddress"
|
||||
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)
|
||||
}
|
||||
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) {
|
||||
invalidMemory := "--memory=invalid"
|
||||
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)
|
||||
}
|
||||
if _, hostconfig := mustParse(t, validMemory); hostconfig.Memory != 1073741824 {
|
||||
|
@ -372,7 +373,7 @@ func TestParseWithMemorySwap(t *testing.T) {
|
|||
invalidMemory := "--memory-swap=invalid"
|
||||
validMemory := "--memory-swap=1G"
|
||||
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)
|
||||
}
|
||||
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"},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -436,7 +437,7 @@ func TestParseWithExpose(t *testing.T) {
|
|||
}
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -475,7 +476,7 @@ func TestParseDevice(t *testing.T) {
|
|||
},
|
||||
}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -491,11 +492,11 @@ func TestParseDevice(t *testing.T) {
|
|||
|
||||
func TestParseModes(t *testing.T) {
|
||||
// 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)
|
||||
}
|
||||
// ipc ok
|
||||
_, hostconfig, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
||||
_, hostconfig, _, _, err := parseRun([]string{"--ipc=host", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -503,11 +504,11 @@ func TestParseModes(t *testing.T) {
|
|||
t.Fatalf("Expected a valid IpcMode, got %v", hostconfig.IpcMode)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// pid ok
|
||||
_, hostconfig, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
||||
_, hostconfig, _, _, err = parseRun([]string{"--pid=host", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -515,11 +516,11 @@ func TestParseModes(t *testing.T) {
|
|||
t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// uts ok
|
||||
_, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
||||
_, hostconfig, _, _, err = parseRun([]string{"--uts=host", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -527,11 +528,11 @@ func TestParseModes(t *testing.T) {
|
|||
t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode)
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// shm-size ok
|
||||
_, hostconfig, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
_, hostconfig, _, _, err = parseRun([]string{"--shm-size=128m", "img", "cmd"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -560,12 +561,12 @@ func TestParseRestartPolicy(t *testing.T) {
|
|||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -577,11 +578,11 @@ func TestParseRestartPolicy(t *testing.T) {
|
|||
|
||||
func TestParseLoggingOpts(t *testing.T) {
|
||||
// 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)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -596,18 +597,18 @@ func TestParseEnvfileVariables(t *testing.T) {
|
|||
e = "open nonexistent: The system cannot find the file specified."
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(config.Env) != 1 || config.Env[0] != "ENV1=value1" {
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -622,18 +623,18 @@ func TestParseLabelfileVariables(t *testing.T) {
|
|||
e = "open nonexistent: The system cannot find the file specified."
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
// 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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(config.Labels) != 1 || config.Labels["LABEL1"] != "value1" {
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -643,7 +644,7 @@ func TestParseLabelfileVariables(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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче