mirror of
https://github.com/clearlinux/docker.git
synced 2026-08-19 12:16:30 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 786b29d4db | |||
| 05cc946f62 | |||
| 0e9ef654d2 | |||
| 635ef94a6d | |||
| 937d37fa1a | |||
| 08c55cd94b | |||
| 10965f373c | |||
| c4e6b44df8 | |||
| cc712e6de4 | |||
| e698770d40 | |||
| ac6a9ce95f | |||
| b03b8a23d2 | |||
| 34815f3fa6 | |||
| 7e8cceb05a | |||
| 72d9ba5ac9 | |||
| fcc44248ef | |||
| c090014053 | |||
| 6906fb0693 | |||
| 50fe5319c4 | |||
| 53b80bdc9c | |||
| 44a8cf337b |
@@ -1,5 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## 1.7.1 (2015-07-14)
|
||||
|
||||
#### Runtime
|
||||
|
||||
- Fix default user spawning exec process with `docker exec`
|
||||
- Make `--bridge=none` not to configure the network bridge
|
||||
- Publish networking stats properly
|
||||
- Fix implicit devicemapper selection with static binaries
|
||||
- Fix socket connections that hung intermittently
|
||||
- Fix bridge interface creation on CentOS/RHEL 6.6
|
||||
- Fix local dns lookups added to resolv.conf
|
||||
- Fix copy command mounting volumes
|
||||
- Fix read/write privileges in volumes mounted with --volumes-from
|
||||
|
||||
#### Remote API
|
||||
|
||||
- Fix unmarshalling of Command and Entrypoint
|
||||
- Set limit for minimum client version supported
|
||||
- Validate port specification
|
||||
- Return proper errors when attach/reattach fail
|
||||
|
||||
#### Distribution
|
||||
|
||||
- Fix pulling private images
|
||||
- Fix fallback between registry V2 and V1
|
||||
|
||||
## 1.7.0 (2015-06-16)
|
||||
|
||||
#### Runtime
|
||||
|
||||
@@ -138,7 +138,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers m
|
||||
if expectedPayload && in == nil {
|
||||
in = bytes.NewReader([]byte{})
|
||||
}
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.Version, path), in)
|
||||
if err != nil {
|
||||
return nil, "", -1, err
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
|
||||
if dockerversion.VERSION != "" {
|
||||
fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
|
||||
}
|
||||
fmt.Fprintf(cli.out, "Client API version: %s\n", api.APIVERSION)
|
||||
fmt.Fprintf(cli.out, "Client API version: %s\n", api.Version)
|
||||
fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version())
|
||||
if dockerversion.GITCOMMIT != "" {
|
||||
fmt.Fprintf(cli.out, "Git commit (client): %s\n", dockerversion.GITCOMMIT)
|
||||
|
||||
+8
-2
@@ -16,8 +16,14 @@ import (
|
||||
|
||||
// Common constants for daemon and client.
|
||||
const (
|
||||
APIVERSION version.Version = "1.19" // Current REST API version
|
||||
DefaultDockerfileName string = "Dockerfile" // Default filename with Docker commands, read by docker build
|
||||
// Current REST API version
|
||||
Version version.Version = "1.19"
|
||||
|
||||
// Minimun REST API version supported
|
||||
MinVersion version.Version = "1.12"
|
||||
|
||||
// Default filename with Docker commands, read by docker build
|
||||
DefaultDockerfileName string = "Dockerfile"
|
||||
)
|
||||
|
||||
type ByPrivatePort []types.Port
|
||||
|
||||
+29
-17
@@ -247,7 +247,7 @@ func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *htt
|
||||
func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
v := &types.Version{
|
||||
Version: dockerversion.VERSION,
|
||||
ApiVersion: api.APIVERSION,
|
||||
ApiVersion: api.Version,
|
||||
GitCommit: dockerversion.GITCOMMIT,
|
||||
GoVersion: runtime.Version(),
|
||||
Os: runtime.GOOS,
|
||||
@@ -1101,6 +1101,11 @@ func (s *Server) postContainersAttach(version version.Version, w http.ResponseWr
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
cont, err := s.daemon.Get(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inStream, outStream, err := hijackServer(w)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1124,7 +1129,7 @@ func (s *Server) postContainersAttach(version version.Version, w http.ResponseWr
|
||||
Multiplex: version.GreaterThanOrEqualTo("1.6"),
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerAttachWithLogs(vars["name"], attachWithLogsConfig); err != nil {
|
||||
if err := s.daemon.ContainerAttachWithLogs(cont, attachWithLogsConfig); err != nil {
|
||||
fmt.Fprintf(outStream, "Error attaching: %s\n", err)
|
||||
}
|
||||
|
||||
@@ -1139,6 +1144,11 @@ func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWrit
|
||||
return fmt.Errorf("Missing parameter")
|
||||
}
|
||||
|
||||
cont, err := s.daemon.Get(vars["name"])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
@@ -1150,7 +1160,7 @@ func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWrit
|
||||
Stream: boolValue(r, "stream"),
|
||||
}
|
||||
|
||||
if err := s.daemon.ContainerWsAttachWithLogs(vars["name"], wsAttachWithLogsConfig); err != nil {
|
||||
if err := s.daemon.ContainerWsAttachWithLogs(cont, wsAttachWithLogsConfig); err != nil {
|
||||
logrus.Errorf("Error attaching websocket: %s", err)
|
||||
}
|
||||
})
|
||||
@@ -1207,18 +1217,17 @@ func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter,
|
||||
|
||||
func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
var (
|
||||
authConfig = &cliconfig.AuthConfig{}
|
||||
configFileEncoded = r.Header.Get("X-Registry-Config")
|
||||
configFile = &cliconfig.ConfigFile{}
|
||||
buildConfig = builder.NewBuildConfig()
|
||||
authConfigs = map[string]cliconfig.AuthConfig{}
|
||||
authConfigsEncoded = r.Header.Get("X-Registry-Config")
|
||||
buildConfig = builder.NewBuildConfig()
|
||||
)
|
||||
|
||||
if configFileEncoded != "" {
|
||||
configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
|
||||
if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
|
||||
if authConfigsEncoded != "" {
|
||||
authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
|
||||
if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil {
|
||||
// for a pull it is not an error if no auth was given
|
||||
// to increase compatibility with the existing api it is defaulting to be empty
|
||||
configFile = &cliconfig.ConfigFile{}
|
||||
// to increase compatibility with the existing api it is defaulting
|
||||
// to be empty.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1245,8 +1254,7 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht
|
||||
buildConfig.SuppressOutput = boolValue(r, "q")
|
||||
buildConfig.NoCache = boolValue(r, "nocache")
|
||||
buildConfig.ForceRemove = boolValue(r, "forcerm")
|
||||
buildConfig.AuthConfig = authConfig
|
||||
buildConfig.ConfigFile = configFile
|
||||
buildConfig.AuthConfigs = authConfigs
|
||||
buildConfig.MemorySwap = int64ValueOrZero(r, "memswap")
|
||||
buildConfig.Memory = int64ValueOrZero(r, "memory")
|
||||
buildConfig.CpuShares = int64ValueOrZero(r, "cpushares")
|
||||
@@ -1475,14 +1483,18 @@ func makeHttpHandler(logging bool, localMethod string, localRoute string, handle
|
||||
}
|
||||
version := version.Version(mux.Vars(r)["version"])
|
||||
if version == "" {
|
||||
version = api.APIVERSION
|
||||
version = api.Version
|
||||
}
|
||||
if corsHeaders != "" {
|
||||
writeCorsHeaders(w, r, corsHeaders)
|
||||
}
|
||||
|
||||
if version.GreaterThan(api.APIVERSION) {
|
||||
http.Error(w, fmt.Errorf("client and server don't have same version (client API version: %s, server API version: %s)", version, api.APIVERSION).Error(), http.StatusBadRequest)
|
||||
if version.GreaterThan(api.Version) {
|
||||
http.Error(w, fmt.Errorf("client is newer than server (client API version: %s, server API version: %s)", version, api.Version).Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if version.LessThan(api.MinVersion) {
|
||||
http.Error(w, fmt.Errorf("client is too old, minimum supported API version is %s, please upgrade your client to a newer version", api.MinVersion).Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ type Builder struct {
|
||||
// the final configs of the Dockerfile but dont want the layers
|
||||
disableCommit bool
|
||||
|
||||
AuthConfig *cliconfig.AuthConfig
|
||||
ConfigFile *cliconfig.ConfigFile
|
||||
// Registry server auth configs used to pull images when handling `FROM`.
|
||||
AuthConfigs map[string]cliconfig.AuthConfig
|
||||
|
||||
// Deprecated, original writer used for ImagePull. To be removed.
|
||||
OutOld io.Writer
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/builder/parser"
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/daemon"
|
||||
"github.com/docker/docker/graph"
|
||||
imagepkg "github.com/docker/docker/image"
|
||||
@@ -446,15 +447,19 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) {
|
||||
tag = "latest"
|
||||
}
|
||||
|
||||
pullRegistryAuth := b.AuthConfig
|
||||
if len(b.ConfigFile.AuthConfigs) > 0 {
|
||||
pullRegistryAuth := &cliconfig.AuthConfig{}
|
||||
if len(b.AuthConfigs) > 0 {
|
||||
// The request came with a full auth config file, we prefer to use that
|
||||
repoInfo, err := b.Daemon.RegistryService.ResolveRepository(remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolvedAuth := registry.ResolveAuthConfig(b.ConfigFile, repoInfo.Index)
|
||||
pullRegistryAuth = &resolvedAuth
|
||||
|
||||
resolvedConfig := registry.ResolveAuthConfig(
|
||||
&cliconfig.ConfigFile{AuthConfigs: b.AuthConfigs},
|
||||
repoInfo.Index,
|
||||
)
|
||||
pullRegistryAuth = &resolvedConfig
|
||||
}
|
||||
|
||||
imagePullConfig := &graph.ImagePullConfig{
|
||||
|
||||
+4
-7
@@ -53,8 +53,7 @@ type Config struct {
|
||||
CpuSetCpus string
|
||||
CpuSetMems string
|
||||
CgroupParent string
|
||||
AuthConfig *cliconfig.AuthConfig
|
||||
ConfigFile *cliconfig.ConfigFile
|
||||
AuthConfigs map[string]cliconfig.AuthConfig
|
||||
|
||||
Stdout io.Writer
|
||||
Context io.ReadCloser
|
||||
@@ -79,9 +78,8 @@ func (b *Config) WaitCancelled() <-chan struct{} {
|
||||
|
||||
func NewBuildConfig() *Config {
|
||||
return &Config{
|
||||
AuthConfig: &cliconfig.AuthConfig{},
|
||||
ConfigFile: &cliconfig.ConfigFile{},
|
||||
cancelled: make(chan struct{}),
|
||||
AuthConfigs: map[string]cliconfig.AuthConfig{},
|
||||
cancelled: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,8 +158,7 @@ func Build(d *daemon.Daemon, buildConfig *Config) error {
|
||||
Pull: buildConfig.Pull,
|
||||
OutOld: buildConfig.Stdout,
|
||||
StreamFormatter: sf,
|
||||
AuthConfig: buildConfig.AuthConfig,
|
||||
ConfigFile: buildConfig.ConfigFile,
|
||||
AuthConfigs: buildConfig.AuthConfigs,
|
||||
dockerfileName: buildConfig.DockerfileName,
|
||||
cpuShares: buildConfig.CpuShares,
|
||||
cpuPeriod: buildConfig.CpuPeriod,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
FROM centos:7
|
||||
|
||||
RUN yum groupinstall -y "Development Tools"
|
||||
RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs
|
||||
RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar
|
||||
|
||||
ENV GO_VERSION 1.4.2
|
||||
|
||||
@@ -38,6 +38,10 @@ for version in "${versions[@]}"; do
|
||||
centos:*)
|
||||
# get "Development Tools" packages dependencies
|
||||
echo 'RUN yum groupinstall -y "Development Tools"' >> "$version/Dockerfile"
|
||||
|
||||
if [[ "$version" == "centos-7" ]]; then
|
||||
echo 'RUN yum -y swap -- remove systemd-container systemd-container-libs -- install systemd systemd-libs' >> "$version/Dockerfile"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo 'RUN yum install -y @development-tools fedora-packager' >> "$version/Dockerfile"
|
||||
|
||||
+2
-12
@@ -14,12 +14,7 @@ type ContainerAttachWithLogsConfig struct {
|
||||
Multiplex bool
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerAttachWithLogs(name string, c *ContainerAttachWithLogsConfig) error {
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerAttachWithLogs(container *Container, c *ContainerAttachWithLogsConfig) error {
|
||||
var errStream io.Writer
|
||||
|
||||
if !container.Config.Tty && c.Multiplex {
|
||||
@@ -51,11 +46,6 @@ type ContainerWsAttachWithLogsConfig struct {
|
||||
Logs, Stream bool
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerWsAttachWithLogs(name string, c *ContainerWsAttachWithLogsConfig) error {
|
||||
container, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (daemon *Daemon) ContainerWsAttachWithLogs(container *Container, c *ContainerWsAttachWithLogsConfig) error {
|
||||
return container.AttachWithLogs(c.InStream, c.OutStream, c.ErrStream, c.Logs, c.Stream)
|
||||
}
|
||||
|
||||
+18
-18
@@ -14,24 +14,24 @@ const (
|
||||
// CommonConfig defines the configuration of a docker daemon which are
|
||||
// common across platforms.
|
||||
type CommonConfig struct {
|
||||
AutoRestart bool
|
||||
Context map[string][]string
|
||||
CorsHeaders string
|
||||
DisableNetwork bool
|
||||
Dns []string
|
||||
DnsSearch []string
|
||||
EnableCors bool
|
||||
ExecDriver string
|
||||
ExecOptions []string
|
||||
ExecRoot string
|
||||
GraphDriver string
|
||||
GraphOptions []string
|
||||
Labels []string
|
||||
LogConfig runconfig.LogConfig
|
||||
Mtu int
|
||||
Pidfile string
|
||||
Root string
|
||||
TrustKeyPath string
|
||||
AutoRestart bool
|
||||
Context map[string][]string
|
||||
CorsHeaders string
|
||||
DisableBridge bool
|
||||
Dns []string
|
||||
DnsSearch []string
|
||||
EnableCors bool
|
||||
ExecDriver string
|
||||
ExecOptions []string
|
||||
ExecRoot string
|
||||
GraphDriver string
|
||||
GraphOptions []string
|
||||
Labels []string
|
||||
LogConfig runconfig.LogConfig
|
||||
Mtu int
|
||||
Pidfile string
|
||||
Root string
|
||||
TrustKeyPath string
|
||||
}
|
||||
|
||||
// InstallCommonFlags adds command-line options to the top-level flag parser for
|
||||
|
||||
+12
-2
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/docker/docker/nat"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/broadcastwriter"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonlog"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
@@ -599,11 +600,20 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range mounts {
|
||||
dest, err := container.GetResourcePath(m.Destination)
|
||||
var dest string
|
||||
dest, err = container.GetResourcePath(m.Destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := mount.Mount(m.Source, dest, "bind", "rbind,ro"); err != nil {
|
||||
var stat os.FileInfo
|
||||
stat, err = os.Stat(m.Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = fileutils.CreateIfNotExists(dest, stat.IsDir()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = mount.Mount(m.Source, dest, "bind", "rbind,ro"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,7 +469,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
if c != nil && !container.daemon.config.DisableNetwork && container.hostConfig.NetworkMode.IsPrivate() {
|
||||
if c != nil && !container.daemon.config.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() {
|
||||
logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress)
|
||||
joinOptions = append(joinOptions, libnetwork.JoinOptionParentUpdate(c.NetworkSettings.EndpointID, ref.Name, container.NetworkSettings.IPAddress))
|
||||
if c.NetworkSettings.EndpointID != "" {
|
||||
@@ -753,6 +753,11 @@ func (container *Container) AllocateNetwork() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if mode.IsBridge() && container.daemon.config.DisableBridge {
|
||||
container.Config.NetworkDisabled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
n, err := container.daemon.netController.NetworkByName(string(mode))
|
||||
@@ -817,10 +822,6 @@ func (container *Container) initializeNetworking() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if container.daemon.config.DisableNetwork {
|
||||
container.Config.NetworkDisabled = true
|
||||
}
|
||||
|
||||
if container.hostConfig.NetworkMode.IsHost() {
|
||||
container.Config.Hostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
@@ -939,7 +940,7 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
|
||||
}
|
||||
|
||||
func (container *Container) ReleaseNetwork() {
|
||||
if container.hostConfig.NetworkMode.IsContainer() || container.daemon.config.DisableNetwork {
|
||||
if container.hostConfig.NetworkMode.IsContainer() || container.Config.NetworkDisabled {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+30
-15
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/docker/docker/daemon/network"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/nat"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/broadcastwriter"
|
||||
"github.com/docker/docker/pkg/fileutils"
|
||||
@@ -682,7 +683,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
||||
if !config.Bridge.EnableIPTables && config.Bridge.EnableIPMasq {
|
||||
config.Bridge.EnableIPMasq = false
|
||||
}
|
||||
config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge
|
||||
config.DisableBridge = config.Bridge.Iface == disableNetworkBridge
|
||||
|
||||
// Check that the system is supported and we have sufficient privileges
|
||||
if runtime.GOOS != "linux" {
|
||||
@@ -819,11 +820,9 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
||||
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
|
||||
}
|
||||
|
||||
if !config.DisableNetwork {
|
||||
d.netController, err = initNetworkController(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error initializing network controller: %v", err)
|
||||
}
|
||||
d.netController, err = initNetworkController(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error initializing network controller: %v", err)
|
||||
}
|
||||
|
||||
graphdbPath := path.Join(config.Root, "linkgraph.db")
|
||||
@@ -911,12 +910,22 @@ func initNetworkController(config *Config) (libnetwork.NetworkController, error)
|
||||
return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
|
||||
}
|
||||
|
||||
// Initialize default driver "bridge"
|
||||
if !config.DisableBridge {
|
||||
// Initialize default driver "bridge"
|
||||
if err := initBridgeDriver(controller, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return controller, nil
|
||||
}
|
||||
|
||||
func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error {
|
||||
option := options.Generic{
|
||||
"EnableIPForwarding": config.Bridge.EnableIPForward}
|
||||
|
||||
if err := controller.ConfigureNetworkDriver("bridge", options.Generic{netlabel.GenericData: option}); err != nil {
|
||||
return nil, fmt.Errorf("Error initializing bridge driver: %v", err)
|
||||
return fmt.Errorf("Error initializing bridge driver: %v", err)
|
||||
}
|
||||
|
||||
netOption := options.Generic{
|
||||
@@ -931,7 +940,7 @@ func initNetworkController(config *Config) (libnetwork.NetworkController, error)
|
||||
if config.Bridge.IP != "" {
|
||||
ip, bipNet, err := net.ParseCIDR(config.Bridge.IP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
bipNet.IP = ip
|
||||
@@ -941,7 +950,7 @@ func initNetworkController(config *Config) (libnetwork.NetworkController, error)
|
||||
if config.Bridge.FixedCIDR != "" {
|
||||
_, fCIDR, err := net.ParseCIDR(config.Bridge.FixedCIDR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
netOption["FixedCIDR"] = fCIDR
|
||||
@@ -950,7 +959,7 @@ func initNetworkController(config *Config) (libnetwork.NetworkController, error)
|
||||
if config.Bridge.FixedCIDRv6 != "" {
|
||||
_, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
netOption["FixedCIDRv6"] = fCIDRv6
|
||||
@@ -970,16 +979,15 @@ func initNetworkController(config *Config) (libnetwork.NetworkController, error)
|
||||
}
|
||||
|
||||
// Initialize default network on "bridge" with the same name
|
||||
_, err = controller.NewNetwork("bridge", "bridge",
|
||||
_, err := controller.NewNetwork("bridge", "bridge",
|
||||
libnetwork.NetworkOptionGeneric(options.Generic{
|
||||
netlabel.GenericData: netOption,
|
||||
netlabel.EnableIPv6: config.Bridge.EnableIPv6,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating default \"bridge\" network: %v", err)
|
||||
return fmt.Errorf("Error creating default \"bridge\" network: %v", err)
|
||||
}
|
||||
|
||||
return controller, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) Shutdown() error {
|
||||
@@ -1190,6 +1198,13 @@ func (daemon *Daemon) verifyHostConfig(hostConfig *runconfig.HostConfig) ([]stri
|
||||
return warnings, nil
|
||||
}
|
||||
|
||||
for port := range hostConfig.PortBindings {
|
||||
_, portStr := nat.SplitProtoPort(string(port))
|
||||
if _, err := nat.ParsePort(portStr); err != nil {
|
||||
return warnings, fmt.Errorf("Invalid port specification: %s", portStr)
|
||||
}
|
||||
}
|
||||
|
||||
if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") {
|
||||
return warnings, fmt.Errorf("Cannot use --lxc-conf with execdriver: %s", daemon.ExecutionDriver().Name())
|
||||
}
|
||||
|
||||
+6
-2
@@ -109,7 +109,6 @@ func (d *Daemon) getActiveContainer(name string) (*Container, error) {
|
||||
}
|
||||
|
||||
func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, error) {
|
||||
|
||||
// Not all drivers support Exec (LXC for example)
|
||||
if err := checkExecSupport(d.execDriver.Name()); err != nil {
|
||||
return "", err
|
||||
@@ -123,11 +122,16 @@ func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, erro
|
||||
cmd := runconfig.NewCommand(config.Cmd...)
|
||||
entrypoint, args := d.getEntrypointAndArgs(runconfig.NewEntrypoint(), cmd)
|
||||
|
||||
user := config.User
|
||||
if len(user) == 0 {
|
||||
user = container.Config.User
|
||||
}
|
||||
|
||||
processConfig := execdriver.ProcessConfig{
|
||||
Tty: config.Tty,
|
||||
Entrypoint: entrypoint,
|
||||
Arguments: args,
|
||||
User: config.User,
|
||||
User: user,
|
||||
}
|
||||
|
||||
execConfig := &execConfig{
|
||||
|
||||
@@ -104,8 +104,8 @@ type DeviceSet struct {
|
||||
thinpBlockSize uint32
|
||||
thinPoolDevice string
|
||||
Transaction `json:"-"`
|
||||
overrideUdevSyncCheck bool
|
||||
deferredRemove bool // use deferred removal
|
||||
overrideUdevSyncCheck bool
|
||||
}
|
||||
|
||||
type DiskUsage struct {
|
||||
@@ -1033,10 +1033,7 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
|
||||
|
||||
// https://github.com/docker/docker/issues/4036
|
||||
if supported := devicemapper.UdevSetSyncSupport(true); !supported {
|
||||
logrus.Errorf("Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option")
|
||||
if !devices.overrideUdevSyncCheck {
|
||||
return graphdriver.ErrNotSupported
|
||||
}
|
||||
logrus.Warn("Udev sync is not supported. This will lead to unexpected behavior, data loss and errors. For more information, see https://docs.docker.com/reference/commandline/cli/#daemon-storage-driver-option")
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(devices.metadataDir(), 0700); err != nil && !os.IsExist(err) {
|
||||
|
||||
@@ -168,7 +168,7 @@ func scanPriorDrivers(root string) []string {
|
||||
priorDrivers := []string{}
|
||||
for driver := range drivers {
|
||||
p := filepath.Join(root, driver)
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
if _, err := os.Stat(p); err == nil && driver != "vfs" {
|
||||
priorDrivers = append(priorDrivers, driver)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libnetwork/sandbox"
|
||||
)
|
||||
|
||||
func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) error {
|
||||
@@ -19,6 +20,10 @@ func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) er
|
||||
var preCpuStats types.CpuStats
|
||||
getStat := func(v interface{}) *types.Stats {
|
||||
update := v.(*execdriver.ResourceStats)
|
||||
// Retrieve the nw statistics from libnetwork and inject them in the Stats
|
||||
if nwStats, err := daemon.getNetworkStats(name); err == nil {
|
||||
update.Stats.Interfaces = nwStats
|
||||
}
|
||||
ss := convertToAPITypes(update.Stats)
|
||||
ss.PreCpuStats = preCpuStats
|
||||
ss.MemoryStats.Limit = uint64(update.MemoryLimit)
|
||||
@@ -118,3 +123,46 @@ func copyBlkioEntry(entries []cgroups.BlkioStatEntry) []types.BlkioStatEntry {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getNetworkStats(name string) ([]*libcontainer.NetworkInterface, error) {
|
||||
var list []*libcontainer.NetworkInterface
|
||||
|
||||
c, err := daemon.Get(name)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
ep, err := nw.EndpointByID(c.NetworkSettings.EndpointID)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
stats, err := ep.Statistics()
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Convert libnetwork nw stats into libcontainer nw stats
|
||||
for ifName, ifStats := range stats {
|
||||
list = append(list, convertLnNetworkStats(ifName, ifStats))
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func convertLnNetworkStats(name string, stats *sandbox.InterfaceStatistics) *libcontainer.NetworkInterface {
|
||||
n := &libcontainer.NetworkInterface{Name: name}
|
||||
n.RxBytes = stats.RxBytes
|
||||
n.RxPackets = stats.RxPackets
|
||||
n.RxErrors = stats.RxErrors
|
||||
n.RxDropped = stats.RxDropped
|
||||
n.TxBytes = stats.TxBytes
|
||||
n.TxPackets = stats.TxPackets
|
||||
n.TxErrors = stats.TxErrors
|
||||
n.TxDropped = stats.TxDropped
|
||||
return n
|
||||
}
|
||||
|
||||
+9
-4
@@ -191,11 +191,16 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
|
||||
}
|
||||
|
||||
for _, m := range c.MountPoints {
|
||||
cp := m
|
||||
cp.RW = m.RW && mode != "ro"
|
||||
cp := &mountPoint{
|
||||
Name: m.Name,
|
||||
Source: m.Source,
|
||||
RW: m.RW && mode != "ro",
|
||||
Driver: m.Driver,
|
||||
Destination: m.Destination,
|
||||
}
|
||||
|
||||
if len(m.Source) == 0 {
|
||||
v, err := createVolume(m.Name, m.Driver)
|
||||
if len(cp.Source) == 0 {
|
||||
v, err := createVolume(cp.Name, cp.Driver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,489 @@
|
||||
<!--[metadata]>
|
||||
+++
|
||||
title = "daemon"
|
||||
description = "The daemon command description and usage"
|
||||
keywords = ["container, daemon, runtime"]
|
||||
[menu.main]
|
||||
parent = "smn_cli"
|
||||
+++
|
||||
<![end-metadata]-->
|
||||
|
||||
# daemon
|
||||
|
||||
Usage: docker [OPTIONS] COMMAND [arg...]
|
||||
|
||||
A self-sufficient runtime for linux containers.
|
||||
|
||||
Options:
|
||||
--api-cors-header="" Set CORS headers in the remote API
|
||||
-b, --bridge="" Attach containers to a network bridge
|
||||
--bip="" Specify network bridge IP
|
||||
-D, --debug=false Enable debug mode
|
||||
-d, --daemon=false Enable daemon mode
|
||||
--default-gateway="" Container default gateway IPv4 address
|
||||
--default-gateway-v6="" Container default gateway IPv6 address
|
||||
--dns=[] DNS server to use
|
||||
--dns-search=[] DNS search domains to use
|
||||
--default-ulimit=[] Set default ulimit settings for containers
|
||||
-e, --exec-driver="native" Exec driver to use
|
||||
--exec-opt=[] Set exec driver options
|
||||
--exec-root="/var/run/docker" Root of the Docker execdriver
|
||||
--fixed-cidr="" IPv4 subnet for fixed IPs
|
||||
--fixed-cidr-v6="" IPv6 subnet for fixed IPs
|
||||
-G, --group="docker" Group for the unix socket
|
||||
-g, --graph="/var/lib/docker" Root of the Docker runtime
|
||||
-H, --host=[] Daemon socket(s) to connect to
|
||||
-h, --help=false Print usage
|
||||
--icc=true Enable inter-container communication
|
||||
--insecure-registry=[] Enable insecure registry communication
|
||||
--ip=0.0.0.0 Default IP when binding container ports
|
||||
--ip-forward=true Enable net.ipv4.ip_forward
|
||||
--ip-masq=true Enable IP masquerading
|
||||
--iptables=true Enable addition of iptables rules
|
||||
--ipv6=false Enable IPv6 networking
|
||||
-l, --log-level="info" Set the logging level
|
||||
--label=[] Set key=value labels to the daemon
|
||||
--log-driver="json-file" Default driver for container logs
|
||||
--log-opt=[] Log driver specific options
|
||||
--mtu=0 Set the containers network MTU
|
||||
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file
|
||||
--registry-mirror=[] Preferred Docker registry mirror
|
||||
-s, --storage-driver="" Storage driver to use
|
||||
--selinux-enabled=false Enable selinux support
|
||||
--storage-opt=[] Set storage driver options
|
||||
--tls=false Use TLS; implied by --tlsverify
|
||||
--tlscacert="~/.docker/ca.pem" Trust certs signed only by this CA
|
||||
--tlscert="~/.docker/cert.pem" Path to TLS certificate file
|
||||
--tlskey="~/.docker/key.pem" Path to TLS key file
|
||||
--tlsverify=false Use TLS and verify the remote
|
||||
--userland-proxy=true Use userland proxy for loopback traffic
|
||||
-v, --version=false Print version information and quit
|
||||
|
||||
Options with [] may be specified multiple times.
|
||||
|
||||
The Docker daemon is the persistent process that manages containers. Docker
|
||||
uses the same binary for both the daemon and client. To run the daemon you
|
||||
provide the `-d` flag.
|
||||
|
||||
To run the daemon with debug output, use `docker -d -D`.
|
||||
|
||||
## Daemon socket option
|
||||
|
||||
The Docker daemon can listen for [Docker Remote API](/reference/api/docker_remote_api/)
|
||||
requests via three different types of Socket: `unix`, `tcp`, and `fd`.
|
||||
|
||||
By default, a `unix` domain socket (or IPC socket) is created at
|
||||
`/var/run/docker.sock`, requiring either `root` permission, or `docker` group
|
||||
membership.
|
||||
|
||||
If you need to access the Docker daemon remotely, you need to enable the `tcp`
|
||||
Socket. Beware that the default setup provides un-encrypted and
|
||||
un-authenticated direct access to the Docker daemon - and should be secured
|
||||
either using the [built in HTTPS encrypted socket](/articles/https/), or by
|
||||
putting a secure web proxy in front of it. You can listen on port `2375` on all
|
||||
network interfaces with `-H tcp://0.0.0.0:2375`, or on a particular network
|
||||
interface using its IP address: `-H tcp://192.168.59.103:2375`. It is
|
||||
conventional to use port `2375` for un-encrypted, and port `2376` for encrypted
|
||||
communication with the daemon.
|
||||
|
||||
> **Note:**
|
||||
> If you're using an HTTPS encrypted socket, keep in mind that only
|
||||
> TLS1.0 and greater are supported. Protocols SSLv3 and under are not
|
||||
> supported anymore for security reasons.
|
||||
|
||||
On Systemd based systems, you can communicate with the daemon via
|
||||
[Systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html),
|
||||
use `docker -d -H fd://`. Using `fd://` will work perfectly for most setups but
|
||||
you can also specify individual sockets: `docker -d -H fd://3`. If the
|
||||
specified socket activated files aren't found, then Docker will exit. You can
|
||||
find examples of using Systemd socket activation with Docker and Systemd in the
|
||||
[Docker source tree](https://github.com/docker/docker/tree/master/contrib/init/systemd/).
|
||||
|
||||
You can configure the Docker daemon to listen to multiple sockets at the same
|
||||
time using multiple `-H` options:
|
||||
|
||||
# listen using the default unix socket, and on 2 specific IP addresses on this host.
|
||||
docker -d -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2
|
||||
|
||||
The Docker client will honor the `DOCKER_HOST` environment variable to set the
|
||||
`-H` flag for the client.
|
||||
|
||||
$ docker -H tcp://0.0.0.0:2375 ps
|
||||
# or
|
||||
$ export DOCKER_HOST="tcp://0.0.0.0:2375"
|
||||
$ docker ps
|
||||
# both are equal
|
||||
|
||||
Setting the `DOCKER_TLS_VERIFY` environment variable to any value other than
|
||||
the empty string is equivalent to setting the `--tlsverify` flag. The following
|
||||
are equivalent:
|
||||
|
||||
$ docker --tlsverify ps
|
||||
# or
|
||||
$ export DOCKER_TLS_VERIFY=1
|
||||
$ docker ps
|
||||
|
||||
The Docker client will honor the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`
|
||||
environment variables (or the lowercase versions thereof). `HTTPS_PROXY` takes
|
||||
precedence over `HTTP_PROXY`.
|
||||
|
||||
### Daemon storage-driver option
|
||||
|
||||
The Docker daemon has support for several different image layer storage
|
||||
drivers: `aufs`, `devicemapper`, `btrfs`, `zfs` and `overlay`.
|
||||
|
||||
The `aufs` driver is the oldest, but is based on a Linux kernel patch-set that
|
||||
is unlikely to be merged into the main kernel. These are also known to cause
|
||||
some serious kernel crashes. However, `aufs` is also the only storage driver
|
||||
that allows containers to share executable and shared library memory, so is a
|
||||
useful choice when running thousands of containers with the same program or
|
||||
libraries.
|
||||
|
||||
The `devicemapper` driver uses thin provisioning and Copy on Write (CoW)
|
||||
snapshots. For each devicemapper graph location – typically
|
||||
`/var/lib/docker/devicemapper` – a thin pool is created based on two block
|
||||
devices, one for data and one for metadata. By default, these block devices
|
||||
are created automatically by using loopback mounts of automatically created
|
||||
sparse files. Refer to [Storage driver options](#storage-driver-options) below
|
||||
for a way how to customize this setup.
|
||||
[~jpetazzo/Resizing Docker containers with the Device Mapper plugin](http://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/)
|
||||
article explains how to tune your existing setup without the use of options.
|
||||
|
||||
The `btrfs` driver is very fast for `docker build` - but like `devicemapper`
|
||||
does not share executable memory between devices. Use
|
||||
`docker -d -s btrfs -g /mnt/btrfs_partition`.
|
||||
|
||||
The `zfs` driver is probably not fast as `btrfs` but has a longer track record
|
||||
on stability. Thanks to `Single Copy ARC` shared blocks between clones will be
|
||||
cached only once. Use `docker -d -s zfs`. To select a different zfs filesystem
|
||||
set `zfs.fsname` option as described in [Storage driver options](#storage-driver-options).
|
||||
|
||||
The `overlay` is a very fast union filesystem. It is now merged in the main
|
||||
Linux kernel as of [3.18.0](https://lkml.org/lkml/2014/10/26/137). Call
|
||||
`docker -d -s overlay` to use it.
|
||||
|
||||
> **Note:**
|
||||
> As promising as `overlay` is, the feature is still quite young and should not
|
||||
> be used in production. Most notably, using `overlay` can cause excessive
|
||||
> inode consumption (especially as the number of images grows), as well as
|
||||
> being incompatible with the use of RPMs.
|
||||
|
||||
> **Note:**
|
||||
> It is currently unsupported on `btrfs` or any Copy on Write filesystem
|
||||
> and should only be used over `ext4` partitions.
|
||||
|
||||
### Storage driver options
|
||||
|
||||
Particular storage-driver can be configured with options specified with
|
||||
`--storage-opt` flags. Options for `devicemapper` are prefixed with `dm` and
|
||||
options for `zfs` start with `zfs`.
|
||||
|
||||
* `dm.thinpooldev`
|
||||
|
||||
Specifies a custom block storage device to use for the thin pool.
|
||||
|
||||
If using a block device for device mapper storage, it is best to use `lvm`
|
||||
to create and manage the thin-pool volume. This volume is then handed to Docker
|
||||
to exclusively create snapshot volumes needed for images and containers.
|
||||
|
||||
Managing the thin-pool outside of Docker makes for the most feature-rich
|
||||
method of having Docker utilize device mapper thin provisioning as the
|
||||
backing storage for Docker's containers. The highlights of the lvm-based
|
||||
thin-pool management feature include: automatic or interactive thin-pool
|
||||
resize support, dynamically changing thin-pool features, automatic thinp
|
||||
metadata checking when lvm activates the thin-pool, etc.
|
||||
|
||||
Example use:
|
||||
|
||||
docker -d --storage-opt dm.thinpooldev=/dev/mapper/thin-pool
|
||||
|
||||
* `dm.basesize`
|
||||
|
||||
Specifies the size to use when creating the base device, which limits the
|
||||
size of images and containers. The default value is 10G. Note, thin devices
|
||||
are inherently "sparse", so a 10G device which is mostly empty doesn't use
|
||||
10 GB of space on the pool. However, the filesystem will use more space for
|
||||
the empty case the larger the device is.
|
||||
|
||||
This value affects the system-wide "base" empty filesystem
|
||||
that may already be initialized and inherited by pulled images. Typically,
|
||||
a change to this value requires additional steps to take effect:
|
||||
|
||||
$ sudo service docker stop
|
||||
$ sudo rm -rf /var/lib/docker
|
||||
$ sudo service docker start
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.basesize=20G
|
||||
|
||||
* `dm.loopdatasize`
|
||||
|
||||
>**Note**: This option configures devicemapper loopback, which should not be used in production.
|
||||
|
||||
Specifies the size to use when creating the loopback file for the
|
||||
"data" device which is used for the thin pool. The default size is
|
||||
100G. The file is sparse, so it will not initially take up this
|
||||
much space.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.loopdatasize=200G
|
||||
|
||||
* `dm.loopmetadatasize`
|
||||
|
||||
>**Note**: This option configures devicemapper loopback, which should not be used in production.
|
||||
|
||||
Specifies the size to use when creating the loopback file for the
|
||||
"metadadata" device which is used for the thin pool. The default size
|
||||
is 2G. The file is sparse, so it will not initially take up
|
||||
this much space.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.loopmetadatasize=4G
|
||||
|
||||
* `dm.fs`
|
||||
|
||||
Specifies the filesystem type to use for the base device. The supported
|
||||
options are "ext4" and "xfs". The default is "ext4"
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.fs=xfs
|
||||
|
||||
* `dm.mkfsarg`
|
||||
|
||||
Specifies extra mkfs arguments to be used when creating the base device.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt "dm.mkfsarg=-O ^has_journal"
|
||||
|
||||
* `dm.mountopt`
|
||||
|
||||
Specifies extra mount options used when mounting the thin devices.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.mountopt=nodiscard
|
||||
|
||||
* `dm.datadev`
|
||||
|
||||
(Deprecated, use `dm.thinpooldev`)
|
||||
|
||||
Specifies a custom blockdevice to use for data for the thin pool.
|
||||
|
||||
If using a block device for device mapper storage, ideally both datadev and
|
||||
metadatadev should be specified to completely avoid using the loopback
|
||||
device.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
|
||||
|
||||
* `dm.metadatadev`
|
||||
|
||||
(Deprecated, use `dm.thinpooldev`)
|
||||
|
||||
Specifies a custom blockdevice to use for metadata for the thin pool.
|
||||
|
||||
For best performance the metadata should be on a different spindle than the
|
||||
data, or even better on an SSD.
|
||||
|
||||
If setting up a new metadata pool it is required to be valid. This can be
|
||||
achieved by zeroing the first 4k to indicate empty metadata, like this:
|
||||
|
||||
$ dd if=/dev/zero of=$metadata_dev bs=4096 count=1
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
|
||||
|
||||
* `dm.blocksize`
|
||||
|
||||
Specifies a custom blocksize to use for the thin pool. The default
|
||||
blocksize is 64K.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.blocksize=512K
|
||||
|
||||
* `dm.blkdiscard`
|
||||
|
||||
Enables or disables the use of blkdiscard when removing devicemapper
|
||||
devices. This is enabled by default (only) if using loopback devices and is
|
||||
required to resparsify the loopback file on image/container removal.
|
||||
|
||||
Disabling this on loopback can lead to *much* faster container removal
|
||||
times, but will make the space used in `/var/lib/docker` directory not be
|
||||
returned to the system for other use when containers are removed.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d --storage-opt dm.blkdiscard=false
|
||||
|
||||
* `dm.override_udev_sync_check`
|
||||
|
||||
Overrides the `udev` synchronization checks between `devicemapper` and `udev`.
|
||||
`udev` is the device manager for the Linux kernel.
|
||||
|
||||
To view the `udev` sync support of a Docker daemon that is using the
|
||||
`devicemapper` driver, run:
|
||||
|
||||
$ docker info
|
||||
[...]
|
||||
Udev Sync Supported: true
|
||||
[...]
|
||||
|
||||
When `udev` sync support is `true`, then `devicemapper` and udev can
|
||||
coordinate the activation and deactivation of devices for containers.
|
||||
|
||||
When `udev` sync support is `false`, a race condition occurs between
|
||||
the`devicemapper` and `udev` during create and cleanup. The race condition
|
||||
results in errors and failures. (For information on these failures, see
|
||||
[docker#4036](https://github.com/docker/docker/issues/4036))
|
||||
|
||||
To allow the `docker` daemon to start, regardless of `udev` sync not being
|
||||
supported, set `dm.override_udev_sync_check` to true:
|
||||
|
||||
$ docker -d --storage-opt dm.override_udev_sync_check=true
|
||||
|
||||
When this value is `true`, the `devicemapper` continues and simply warns
|
||||
you the errors are happening.
|
||||
|
||||
> **Note:**
|
||||
> The ideal is to pursue a `docker` daemon and environment that does
|
||||
> support synchronizing with `udev`. For further discussion on this
|
||||
> topic, see [docker#4036](https://github.com/docker/docker/issues/4036).
|
||||
> Otherwise, set this flag for migrating existing Docker daemons to
|
||||
> a daemon with a supported environment.
|
||||
|
||||
|
||||
## Docker execdriver option
|
||||
|
||||
Currently supported options of `zfs`:
|
||||
|
||||
* `zfs.fsname`
|
||||
|
||||
Set zfs filesystem under which docker will create its own datasets.
|
||||
By default docker will pick up the zfs filesystem where docker graph
|
||||
(`/var/lib/docker`) is located.
|
||||
|
||||
Example use:
|
||||
|
||||
$ docker -d -s zfs --storage-opt zfs.fsname=zroot/docker
|
||||
|
||||
## Docker execdriver option
|
||||
|
||||
The Docker daemon uses a specifically built `libcontainer` execution driver as
|
||||
its interface to the Linux kernel `namespaces`, `cgroups`, and `SELinux`.
|
||||
|
||||
There is still legacy support for the original [LXC userspace tools](
|
||||
https://linuxcontainers.org/) via the `lxc` execution driver, however, this is
|
||||
not where the primary development of new functionality is taking place.
|
||||
Add `-e lxc` to the daemon flags to use the `lxc` execution driver.
|
||||
|
||||
## Options for the native execdriver
|
||||
|
||||
You can configure the `native` (libcontainer) execdriver using options specified
|
||||
with the `--exec-opt` flag. All the flag's options have the `native` prefix. A
|
||||
single `native.cgroupdriver` option is available.
|
||||
|
||||
The `native.cgroupdriver` option specifies the management of the container's
|
||||
cgroups. You can specify `cgroupfs` or `systemd`. If you specify `systemd` and
|
||||
it is not available, the system uses `cgroupfs`. By default, if no option is
|
||||
specified, the execdriver first tries `systemd` and falls back to `cgroupfs`.
|
||||
This example sets the execdriver to `cgroupfs`:
|
||||
|
||||
$ sudo docker -d --exec-opt native.cgroupdriver=cgroupfs
|
||||
|
||||
Setting this option applies to all containers the daemon launches.
|
||||
|
||||
## Daemon DNS options
|
||||
|
||||
To set the DNS server for all Docker containers, use
|
||||
`docker -d --dns 8.8.8.8`.
|
||||
|
||||
To set the DNS search domain for all Docker containers, use
|
||||
`docker -d --dns-search example.com`.
|
||||
|
||||
## Insecure registries
|
||||
|
||||
Docker considers a private registry either secure or insecure. In the rest of
|
||||
this section, *registry* is used for *private registry*, and `myregistry:5000`
|
||||
is a placeholder example for a private registry.
|
||||
|
||||
A secure registry uses TLS and a copy of its CA certificate is placed on the
|
||||
Docker host at `/etc/docker/certs.d/myregistry:5000/ca.crt`. An insecure
|
||||
registry is either not using TLS (i.e., listening on plain text HTTP), or is
|
||||
using TLS with a CA certificate not known by the Docker daemon. The latter can
|
||||
happen when the certificate was not found under
|
||||
`/etc/docker/certs.d/myregistry:5000/`, or if the certificate verification
|
||||
failed (i.e., wrong CA).
|
||||
|
||||
By default, Docker assumes all, but local (see local registries below),
|
||||
registries are secure. Communicating with an insecure registry is not possible
|
||||
if Docker assumes that registry is secure. In order to communicate with an
|
||||
insecure registry, the Docker daemon requires `--insecure-registry` in one of
|
||||
the following two forms:
|
||||
|
||||
* `--insecure-registry myregistry:5000` tells the Docker daemon that
|
||||
myregistry:5000 should be considered insecure.
|
||||
* `--insecure-registry 10.1.0.0/16` tells the Docker daemon that all registries
|
||||
whose domain resolve to an IP address is part of the subnet described by the
|
||||
CIDR syntax, should be considered insecure.
|
||||
|
||||
The flag can be used multiple times to allow multiple registries to be marked
|
||||
as insecure.
|
||||
|
||||
If an insecure registry is not marked as insecure, `docker pull`,
|
||||
`docker push`, and `docker search` will result in an error message prompting
|
||||
the user to either secure or pass the `--insecure-registry` flag to the Docker
|
||||
daemon as described above.
|
||||
|
||||
Local registries, whose IP address falls in the 127.0.0.0/8 range, are
|
||||
automatically marked as insecure as of Docker 1.3.2. It is not recommended to
|
||||
rely on this, as it may change in the future.
|
||||
|
||||
## Running a Docker daemon behind a HTTPS_PROXY
|
||||
|
||||
When running inside a LAN that uses a `HTTPS` proxy, the Docker Hub
|
||||
certificates will be replaced by the proxy's certificates. These certificates
|
||||
need to be added to your Docker host's configuration:
|
||||
|
||||
1. Install the `ca-certificates` package for your distribution
|
||||
2. Ask your network admin for the proxy's CA certificate and append them to
|
||||
`/etc/pki/tls/certs/ca-bundle.crt`
|
||||
3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker -d`.
|
||||
The `username:` and `password@` are optional - and are only needed if your
|
||||
proxy is set up to require authentication.
|
||||
|
||||
This will only add the proxy and authentication to the Docker daemon's requests -
|
||||
your `docker build`s and running containers will need extra configuration to
|
||||
use the proxy
|
||||
|
||||
## Default Ulimits
|
||||
|
||||
`--default-ulimit` allows you to set the default `ulimit` options to use for
|
||||
all containers. It takes the same options as `--ulimit` for `docker run`. If
|
||||
these defaults are not set, `ulimit` settings will be inherited, if not set on
|
||||
`docker run`, from the Docker daemon. Any `--ulimit` options passed to
|
||||
`docker run` will overwrite these defaults.
|
||||
|
||||
## Miscellaneous options
|
||||
|
||||
IP masquerading uses address translation to allow containers without a public
|
||||
IP to talk to other machines on the Internet. This may interfere with some
|
||||
network topologies and can be disabled with --ip-masq=false.
|
||||
|
||||
Docker supports softlinks for the Docker data directory (`/var/lib/docker`) and
|
||||
for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be
|
||||
set like this:
|
||||
|
||||
DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
|
||||
# or
|
||||
export DOCKER_TMPDIR=/mnt/disk2/tmp
|
||||
/usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
|
||||
|
||||
|
||||
+14
-6
@@ -29,9 +29,10 @@ import (
|
||||
|
||||
// A Graph is a store for versioned filesystem images and the relationship between them.
|
||||
type Graph struct {
|
||||
Root string
|
||||
idIndex *truncindex.TruncIndex
|
||||
driver graphdriver.Driver
|
||||
Root string
|
||||
idIndex *truncindex.TruncIndex
|
||||
driver graphdriver.Driver
|
||||
imageMutex imageMutex // protect images in driver.
|
||||
}
|
||||
|
||||
// NewGraph instantiates a new graph at the given root path in the filesystem.
|
||||
@@ -145,6 +146,15 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain
|
||||
|
||||
// Register imports a pre-existing image into the graph.
|
||||
func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
|
||||
if err := image.ValidateID(img.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need this entire operation to be atomic within the engine. Note that
|
||||
// this doesn't mean Register is fully safe yet.
|
||||
graph.imageMutex.Lock(img.ID)
|
||||
defer graph.imageMutex.Unlock(img.ID)
|
||||
|
||||
defer func() {
|
||||
// If any error occurs, remove the new dir from the driver.
|
||||
// Don't check for errors since the dir might not have been created.
|
||||
@@ -153,9 +163,7 @@ func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader)
|
||||
graph.driver.Remove(img.ID)
|
||||
}
|
||||
}()
|
||||
if err := image.ValidateID(img.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// (This is a convenience to save time. Race conditions are taken care of by os.Rename)
|
||||
if graph.Exists(img.ID) {
|
||||
return fmt.Errorf("Image %s already exists", img.ID)
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package graph
|
||||
|
||||
import "sync"
|
||||
|
||||
// imageMutex provides a lock per image id to protect shared resources in the
|
||||
// graph. This is only used with registration but should be used when
|
||||
// manipulating the layer store.
|
||||
type imageMutex struct {
|
||||
mus map[string]*sync.Mutex // mutexes by image id.
|
||||
mu sync.Mutex // protects lock map
|
||||
|
||||
// NOTE(stevvooe): The map above will grow to the size of all images ever
|
||||
// registered during a daemon run. To free these resources, we must
|
||||
// deallocate after unlock. Doing this safely is non-trivial in the face
|
||||
// of a very minor leak.
|
||||
}
|
||||
|
||||
// Lock the provided id.
|
||||
func (im *imageMutex) Lock(id string) {
|
||||
im.getImageLock(id).Lock()
|
||||
}
|
||||
|
||||
// Unlock the provided id.
|
||||
func (im *imageMutex) Unlock(id string) {
|
||||
im.getImageLock(id).Unlock()
|
||||
}
|
||||
|
||||
// getImageLock returns the mutex for the given id. This method will never
|
||||
// return nil.
|
||||
func (im *imageMutex) getImageLock(id string) *sync.Mutex {
|
||||
im.mu.Lock()
|
||||
defer im.mu.Unlock()
|
||||
|
||||
if im.mus == nil { // lazy
|
||||
im.mus = make(map[string]*sync.Mutex)
|
||||
}
|
||||
|
||||
mu, ok := im.mus[id]
|
||||
if !ok {
|
||||
mu = new(sync.Mutex)
|
||||
im.mus[id] = mu
|
||||
}
|
||||
|
||||
return mu
|
||||
}
|
||||
@@ -36,10 +36,7 @@ Requires(preun): initscripts
|
||||
# required packages on install
|
||||
Requires: /bin/sh
|
||||
Requires: iptables
|
||||
Requires: libc.so.6
|
||||
Requires: libcgroup
|
||||
Requires: libpthread.so.0
|
||||
Requires: libsqlite3.so.0
|
||||
Requires: tar
|
||||
Requires: xz
|
||||
%if 0%{?fedora} >= 21
|
||||
|
||||
+3
-3
@@ -55,9 +55,9 @@ clone hg code.google.com/p/go.net 84a4013f96e0
|
||||
clone hg code.google.com/p/gosqlite 74691fb6f837
|
||||
|
||||
#get libnetwork packages
|
||||
clone git github.com/docker/libnetwork b116b5c0d20ee4021297fa92b7db4429a622c044
|
||||
clone git github.com/vishvananda/netns 5478c060110032f972e86a1f844fdb9a2f008f2c
|
||||
clone git github.com/vishvananda/netlink 8eb64238879fed52fd51c5b30ad20b928fb4c36c
|
||||
clone git github.com/docker/libnetwork 3daf67270570c1e07e3e3184d46a10f0c5d66f87
|
||||
clone git github.com/vishvananda/netns 493029407eeb434d0c2d44e02ea072ff2488d322
|
||||
clone git github.com/vishvananda/netlink 20397a138846e4d6590e01783ed023ed7e1c38a6
|
||||
|
||||
# get distribution packages
|
||||
clone git github.com/docker/distribution b9eeb328080d367dbde850ec6e94f1e4ac2b5efe
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -77,3 +78,24 @@ func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) {
|
||||
c.Fatal("Expected output on websocket to match input")
|
||||
}
|
||||
}
|
||||
|
||||
// regression gh14320
|
||||
func (s *DockerSuite) TestPostContainersAttachContainerNotFound(c *check.C) {
|
||||
status, body, err := sockRequest("POST", "/containers/doesnotexist/attach", nil)
|
||||
c.Assert(status, check.Equals, http.StatusNotFound)
|
||||
c.Assert(err, check.IsNil)
|
||||
expected := "no such id: doesnotexist\n"
|
||||
if !strings.Contains(string(body), expected) {
|
||||
c.Fatalf("Expected response body to contain %q", expected)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *check.C) {
|
||||
status, body, err := sockRequest("GET", "/containers/doesnotexist/attach/ws", nil)
|
||||
c.Assert(status, check.Equals, http.StatusNotFound)
|
||||
c.Assert(err, check.IsNil)
|
||||
expected := "no such id: doesnotexist\n"
|
||||
if !strings.Contains(string(body), expected) {
|
||||
c.Fatalf("Expected response body to contain %q", expected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,6 +998,30 @@ func (s *DockerSuite) TestContainerApiVerifyHeader(c *check.C) {
|
||||
body.Close()
|
||||
}
|
||||
|
||||
//Issue 14230. daemon should return 500 for invalid port syntax
|
||||
func (s *DockerSuite) TestContainerApiInvalidPortSyntax(c *check.C) {
|
||||
config := `{
|
||||
"Image": "busybox",
|
||||
"HostConfig": {
|
||||
"PortBindings": {
|
||||
"19039;1230": [
|
||||
{}
|
||||
]
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
|
||||
c.Assert(res.StatusCode, check.Equals, http.StatusInternalServerError)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
b, err := readBody(body)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
c.Assert(strings.Contains(string(b[:]), "Invalid port"), check.Equals, true)
|
||||
}
|
||||
|
||||
// Issue 7941 - test to make sure a "null" in JSON is just ignored.
|
||||
// W/o this fix a null in JSON would be parsed into a string var as "null"
|
||||
func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) {
|
||||
@@ -1501,3 +1525,48 @@ func (s *DockerSuite) TestPostContainerStop(c *check.C) {
|
||||
c.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// #14170
|
||||
func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceEntrypoint(c *check.C) {
|
||||
config := struct {
|
||||
Image string
|
||||
Entrypoint string
|
||||
Cmd []string
|
||||
}{"busybox", "echo", []string{"hello", "world"}}
|
||||
_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
|
||||
c.Assert(err, check.IsNil)
|
||||
out, _ := dockerCmd(c, "start", "-a", "echotest")
|
||||
c.Assert(strings.TrimSpace(out), check.Equals, "hello world")
|
||||
|
||||
config2 := struct {
|
||||
Image string
|
||||
Entrypoint []string
|
||||
Cmd []string
|
||||
}{"busybox", []string{"echo"}, []string{"hello", "world"}}
|
||||
_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
|
||||
c.Assert(err, check.IsNil)
|
||||
out, _ = dockerCmd(c, "start", "-a", "echotest2")
|
||||
c.Assert(strings.TrimSpace(out), check.Equals, "hello world")
|
||||
}
|
||||
|
||||
// #14170
|
||||
func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
|
||||
config := struct {
|
||||
Image string
|
||||
Entrypoint string
|
||||
Cmd string
|
||||
}{"busybox", "echo", "hello world"}
|
||||
_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
|
||||
c.Assert(err, check.IsNil)
|
||||
out, _ := dockerCmd(c, "start", "-a", "echotest")
|
||||
c.Assert(strings.TrimSpace(out), check.Equals, "hello world")
|
||||
|
||||
config2 := struct {
|
||||
Image string
|
||||
Cmd []string
|
||||
}{"busybox", []string{"echo", "hello", "world"}}
|
||||
_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
|
||||
c.Assert(err, check.IsNil)
|
||||
out, _ = dockerCmd(c, "start", "-a", "echotest2")
|
||||
c.Assert(strings.TrimSpace(out), check.Equals, "hello world")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
@@ -33,3 +35,41 @@ func (s *DockerSuite) TestCliStatsNoStreamGetCpu(c *check.C) {
|
||||
c.Fatalf("docker stats with no-stream get cpu usage failed: was %v", cpuPercent)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestApiNetworkStats(c *check.C) {
|
||||
// Run container for 30 secs
|
||||
out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
|
||||
id := strings.TrimSpace(out)
|
||||
err := waitRun(id)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
// Retrieve the container address
|
||||
contIP := findContainerIP(c, id)
|
||||
numPings := 10
|
||||
|
||||
// Get the container networking stats before and after pinging the container
|
||||
nwStatsPre := getNetworkStats(c, id)
|
||||
_, err = exec.Command("ping", contIP, "-c", strconv.Itoa(numPings)).Output()
|
||||
c.Assert(err, check.IsNil)
|
||||
nwStatsPost := getNetworkStats(c, id)
|
||||
|
||||
// Verify the stats contain at least the expected number of packets (account for ARP)
|
||||
expRxPkts := 1 + nwStatsPre.RxPackets + uint64(numPings)
|
||||
expTxPkts := 1 + nwStatsPre.TxPackets + uint64(numPings)
|
||||
c.Assert(nwStatsPost.TxPackets >= expTxPkts, check.Equals, true,
|
||||
check.Commentf("Reported less TxPackets than expected. Expected >= %d. Found %d", expTxPkts, nwStatsPost.TxPackets))
|
||||
c.Assert(nwStatsPost.RxPackets >= expRxPkts, check.Equals, true,
|
||||
check.Commentf("Reported less Txbytes than expected. Expected >= %d. Found %d", expRxPkts, nwStatsPost.RxPackets))
|
||||
}
|
||||
|
||||
func getNetworkStats(c *check.C, id string) types.Network {
|
||||
var st *types.Stats
|
||||
|
||||
_, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id), nil, "")
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
err = json.NewDecoder(body).Decode(&st)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
return st.Network
|
||||
}
|
||||
|
||||
@@ -3,15 +3,18 @@ package main
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/go-check/check"
|
||||
)
|
||||
|
||||
func (s *DockerSuite) TestApiOptionsRoute(c *check.C) {
|
||||
status, _, err := sockRequest("OPTIONS", "/", nil)
|
||||
c.Assert(status, check.Equals, http.StatusOK)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(status, check.Equals, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestApiGetEnabledCors(c *check.C) {
|
||||
@@ -26,7 +29,7 @@ func (s *DockerSuite) TestApiGetEnabledCors(c *check.C) {
|
||||
//c.Assert(res.Header.Get("Access-Control-Allow-Headers"), check.Equals, "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestVersionStatusCode(c *check.C) {
|
||||
func (s *DockerSuite) TestApiVersionStatusCode(c *check.C) {
|
||||
conn, err := sockConn(time.Duration(10 * time.Second))
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
@@ -40,3 +43,31 @@ func (s *DockerSuite) TestVersionStatusCode(c *check.C) {
|
||||
res, err := client.Do(req)
|
||||
c.Assert(res.StatusCode, check.Equals, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestApiClientVersionNewerThanServer(c *check.C) {
|
||||
v := strings.Split(string(api.Version), ".")
|
||||
vMinInt, err := strconv.Atoi(v[1])
|
||||
c.Assert(err, check.IsNil)
|
||||
vMinInt++
|
||||
v[1] = strconv.Itoa(vMinInt)
|
||||
version := strings.Join(v, ".")
|
||||
|
||||
status, body, err := sockRequest("GET", "/v"+version+"/version", nil)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(status, check.Equals, http.StatusBadRequest)
|
||||
c.Assert(len(string(body)), check.Not(check.Equals), 0) // Expected not empty body
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestApiClientVersionOldNotSupported(c *check.C) {
|
||||
v := strings.Split(string(api.MinVersion), ".")
|
||||
vMinInt, err := strconv.Atoi(v[1])
|
||||
c.Assert(err, check.IsNil)
|
||||
vMinInt--
|
||||
v[1] = strconv.Itoa(vMinInt)
|
||||
version := strings.Join(v, ".")
|
||||
|
||||
status, body, err := sockRequest("GET", "/v"+version+"/version", nil)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(status, check.Equals, http.StatusBadRequest)
|
||||
c.Assert(len(string(body)), check.Not(check.Equals), 0) // Expected not empty body
|
||||
}
|
||||
|
||||
@@ -632,3 +632,20 @@ func (s *DockerSuite) TestCopyAndRestart(c *check.C) {
|
||||
c.Fatalf("expected %q but got %q", expectedMsg, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestCopyCreatedContainer(c *check.C) {
|
||||
out, err := exec.Command(dockerBinary, "create", "--name", "test_cp", "-v", "/test", "busybox").CombinedOutput()
|
||||
if err != nil {
|
||||
c.Fatalf(string(out), err)
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "test")
|
||||
if err != nil {
|
||||
c.Fatalf("unable to make temporary directory: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
out, err = exec.Command(dockerBinary, "cp", "test_cp:/bin/sh", tmpDir).CombinedOutput()
|
||||
if err != nil {
|
||||
c.Fatalf(string(out), err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1207,7 +1207,12 @@ func (s *DockerDaemonSuite) TestRunContainerWithBridgeNone(c *check.C) {
|
||||
out, err := s.d.Cmd("run", "--rm", "busybox", "ip", "l")
|
||||
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
||||
c.Assert(strings.Contains(out, "eth0"), check.Equals, false,
|
||||
check.Commentf("There shouldn't be eth0 in container when network is disabled: %s", out))
|
||||
check.Commentf("There shouldn't be eth0 in container in default(bridge) mode when bridge network is disabled: %s", out))
|
||||
|
||||
out, err = s.d.Cmd("run", "--rm", "--net=host", "busybox", "ip", "l")
|
||||
c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
|
||||
c.Assert(strings.Contains(out, "eth0"), check.Equals, true,
|
||||
check.Commentf("There should be eth0 in container when --net=host when bridge network is disabled: %s", out))
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *check.C) {
|
||||
|
||||
@@ -444,7 +444,6 @@ func (s *DockerSuite) TestInspectExecID(c *check.C) {
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *check.C) {
|
||||
|
||||
var out string
|
||||
out, _ = dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
|
||||
idA := strings.TrimSpace(out)
|
||||
@@ -609,7 +608,6 @@ func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) {
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestExecWithUser(c *check.C) {
|
||||
|
||||
runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "parent", "busybox", "top")
|
||||
if out, _, err := runCommandWithOutput(runCmd); err != nil {
|
||||
c.Fatal(out, err)
|
||||
@@ -634,3 +632,22 @@ func (s *DockerSuite) TestExecWithUser(c *check.C) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestExecWithImageUser(c *check.C) {
|
||||
name := "testbuilduser"
|
||||
_, err := buildImage(name,
|
||||
`FROM busybox
|
||||
RUN echo 'dockerio:x:1001:1001::/bin:/bin/false' >> /etc/passwd
|
||||
USER dockerio`,
|
||||
true)
|
||||
if err != nil {
|
||||
c.Fatalf("Could not build image %s: %v", name, err)
|
||||
}
|
||||
|
||||
dockerCmd(c, "run", "-d", "--name", "dockerioexec", name, "top")
|
||||
|
||||
out, _ := dockerCmd(c, "exec", "dockerioexec", "whoami")
|
||||
if !strings.Contains(out, "dockerio") {
|
||||
c.Fatalf("exec with user by id expected dockerio user got %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3211,3 +3211,21 @@ func (s *DockerSuite) TestDevicePermissions(c *check.C) {
|
||||
c.Fatalf("output should begin with %q, got %q", permissions, out)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/docker/docker/pull/14498
|
||||
func (s *DockerSuite) TestVolumeFromMixedRWOptions(c *check.C) {
|
||||
dockerCmd(c, "run", "--name", "parent", "-v", "/test", "busybox", "true")
|
||||
dockerCmd(c, "run", "--volumes-from", "parent:ro", "--name", "test-volumes-1", "busybox", "true")
|
||||
dockerCmd(c, "run", "--volumes-from", "parent:rw", "--name", "test-volumes-2", "busybox", "true")
|
||||
|
||||
testRO, err := inspectFieldMap("test-volumes-1", ".VolumesRW", "/test")
|
||||
c.Assert(err, check.IsNil)
|
||||
if testRO != "false" {
|
||||
c.Fatalf("Expected RO volume was RW")
|
||||
}
|
||||
testRW, err := inspectFieldMap("test-volumes-2", ".VolumesRW", "/test")
|
||||
c.Assert(err, check.IsNil)
|
||||
if testRW != "true" {
|
||||
c.Fatalf("Expected RW volume was RO")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,6 +369,45 @@ for data and metadata:
|
||||
--storage-opt dm.metadatadev=/dev/vdc \
|
||||
--storage-opt dm.basesize=20G
|
||||
|
||||
#### dm.override_udev_sync_check
|
||||
|
||||
By default, the devicemapper backend attempts to synchronize with the
|
||||
`udev` device manager for the Linux kernel. This option allows
|
||||
disabling that synchronization, to continue even though the
|
||||
configuration may be buggy.
|
||||
|
||||
To view the `udev` sync support of a Docker daemon that is using the
|
||||
`devicemapper` driver, run:
|
||||
|
||||
$ docker info
|
||||
[...]
|
||||
Udev Sync Supported: true
|
||||
[...]
|
||||
|
||||
When `udev` sync support is `true`, then `devicemapper` and `udev` can
|
||||
coordinate the activation and deactivation of devices for containers.
|
||||
|
||||
When `udev` sync support is `false`, a race condition occurs between
|
||||
the`devicemapper` and `udev` during create and cleanup. The race
|
||||
condition results in errors and failures. (For information on these
|
||||
failures, see
|
||||
[docker#4036](https://github.com/docker/docker/issues/4036))
|
||||
|
||||
To allow the `docker` daemon to start, regardless of whether `udev` sync is
|
||||
`false`, set `dm.override_udev_sync_check` to true:
|
||||
|
||||
$ docker -d --storage-opt dm.override_udev_sync_check=true
|
||||
|
||||
When this value is `true`, the driver continues and simply warns you
|
||||
the errors are happening.
|
||||
|
||||
**Note**: The ideal is to pursue a `docker` daemon and environment
|
||||
that does support synchronizing with `udev`. For further discussion on
|
||||
this topic, see
|
||||
[docker#4036](https://github.com/docker/docker/issues/4036).
|
||||
Otherwise, set this flag for migrating existing Docker daemons to a
|
||||
daemon with a supported environment.
|
||||
|
||||
# EXEC DRIVER OPTIONS
|
||||
|
||||
Use the **--exec-opt** flags to specify options to the exec-driver. The only
|
||||
|
||||
@@ -168,3 +168,23 @@ func ReadSymlinkedDirectory(path string) (string, error) {
|
||||
}
|
||||
return realPath, nil
|
||||
}
|
||||
|
||||
// CreateIfNotExists creates a file or a directory only if it does not already exist.
|
||||
func CreateIfNotExists(path string, isDir bool) error {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if isDir {
|
||||
return os.MkdirAll(path, 0755)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_CREATE, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+3
-3
@@ -154,9 +154,9 @@ func NewSession(client *http.Client, authConfig *cliconfig.AuthConfig, endpoint
|
||||
}
|
||||
}
|
||||
|
||||
if endpoint.Version == APIVersion1 {
|
||||
client.Transport = AuthTransport(client.Transport, authConfig, alwaysSetBasicAuth)
|
||||
}
|
||||
// Annotate the transport unconditionally so that v2 can
|
||||
// properly fallback on v1 when an image is not found.
|
||||
client.Transport = AuthTransport(client.Transport, authConfig, alwaysSetBasicAuth)
|
||||
|
||||
jar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
|
||||
+10
-2
@@ -32,7 +32,11 @@ func (e *Entrypoint) UnmarshalJSON(b []byte) error {
|
||||
|
||||
p := make([]string, 0, 1)
|
||||
if err := json.Unmarshal(b, &p); err != nil {
|
||||
p = append(p, string(b))
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
p = append(p, s)
|
||||
}
|
||||
e.parts = p
|
||||
return nil
|
||||
@@ -79,7 +83,11 @@ func (e *Command) UnmarshalJSON(b []byte) error {
|
||||
|
||||
p := make([]string, 0, 1)
|
||||
if err := json.Unmarshal(b, &p); err != nil {
|
||||
p = append(p, string(b))
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
p = append(p, s)
|
||||
}
|
||||
e.parts = p
|
||||
return nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package runconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@@ -299,3 +300,83 @@ func TestDecodeContainerConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntrypointUnmarshalString(t *testing.T) {
|
||||
var e *Entrypoint
|
||||
echo, err := json.Marshal("echo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal(echo, &e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
slice := e.Slice()
|
||||
if len(slice) != 1 {
|
||||
t.Fatalf("expected 1 element after unmarshal: %q", slice)
|
||||
}
|
||||
|
||||
if slice[0] != "echo" {
|
||||
t.Fatalf("expected `echo`, got: %q", slice[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntrypointUnmarshalSlice(t *testing.T) {
|
||||
var e *Entrypoint
|
||||
echo, err := json.Marshal([]string{"echo"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal(echo, &e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
slice := e.Slice()
|
||||
if len(slice) != 1 {
|
||||
t.Fatalf("expected 1 element after unmarshal: %q", slice)
|
||||
}
|
||||
|
||||
if slice[0] != "echo" {
|
||||
t.Fatalf("expected `echo`, got: %q", slice[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandUnmarshalSlice(t *testing.T) {
|
||||
var e *Command
|
||||
echo, err := json.Marshal([]string{"echo"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal(echo, &e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
slice := e.Slice()
|
||||
if len(slice) != 1 {
|
||||
t.Fatalf("expected 1 element after unmarshal: %q", slice)
|
||||
}
|
||||
|
||||
if slice[0] != "echo" {
|
||||
t.Fatalf("expected `echo`, got: %q", slice[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandUnmarshalString(t *testing.T) {
|
||||
var e *Command
|
||||
echo, err := json.Marshal("echo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal(echo, &e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
slice := e.Slice()
|
||||
if len(slice) != 1 {
|
||||
t.Fatalf("expected 1 element after unmarshal: %q", slice)
|
||||
}
|
||||
|
||||
if slice[0] != "echo" {
|
||||
t.Fatalf("expected `echo`, got: %q", slice[0])
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ImportPath": "github.com/docker/libnetwork",
|
||||
"GoVersion": "go1.4.1",
|
||||
"GoVersion": "go1.4.2",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
@@ -55,6 +55,11 @@
|
||||
"Comment": "v1.4.1-3479-ga9172f5",
|
||||
"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libcontainer/netlink",
|
||||
"Comment": "v1.4.0-495-g3e66118",
|
||||
"Rev": "3e661186ba24f259d3860f067df052c7f6904bee"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libcontainer/user",
|
||||
"Comment": "v1.4.0-495-g3e66118",
|
||||
@@ -75,11 +80,11 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netlink",
|
||||
"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"
|
||||
"Rev": "20397a138846e4d6590e01783ed023ed7e1c38a6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netns",
|
||||
"Rev": "5478c060110032f972e86a1f844fdb9a2f008f2c"
|
||||
"Rev": "493029407eeb434d0c2d44e02ea072ff2488d322"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package bridge
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
bri "github.com/docker/libcontainer/netlink"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipallocator"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
@@ -397,6 +399,20 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func addToBridge(ifaceName, bridgeName string) error {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
|
||||
}
|
||||
|
||||
master, err := net.InterfaceByName(bridgeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find bridge %s: %v", bridgeName, err)
|
||||
}
|
||||
|
||||
return bri.AddToBridge(iface, master)
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
|
||||
var (
|
||||
ipv6Addr *net.IPNet
|
||||
@@ -461,27 +477,27 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}()
|
||||
|
||||
// Generate a name for what will be the host side pipe interface
|
||||
name1, err := generateIfaceName()
|
||||
hostIfName, err := generateIfaceName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate a name for what will be the sandbox side pipe interface
|
||||
name2, err := generateIfaceName()
|
||||
containerIfName, err := generateIfaceName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate and add the interface pipe host <-> sandbox
|
||||
veth := &netlink.Veth{
|
||||
LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
|
||||
PeerName: name2}
|
||||
LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0},
|
||||
PeerName: containerIfName}
|
||||
if err = netlink.LinkAdd(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the host side pipe interface handler
|
||||
host, err := netlink.LinkByName(name1)
|
||||
host, err := netlink.LinkByName(hostIfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -492,7 +508,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}()
|
||||
|
||||
// Get the sandbox side pipe interface handler
|
||||
sbox, err := netlink.LinkByName(name2)
|
||||
sbox, err := netlink.LinkByName(containerIfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -515,9 +531,8 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}
|
||||
|
||||
// Attach host side pipe interface into the bridge
|
||||
if err = netlink.LinkSetMaster(host,
|
||||
&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
|
||||
return err
|
||||
if err = addToBridge(hostIfName, config.BridgeName); err != nil {
|
||||
return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
|
||||
}
|
||||
|
||||
if !config.EnableUserlandProxy {
|
||||
@@ -534,14 +549,24 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
}
|
||||
ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
|
||||
|
||||
// Down the interface before configuring mac address.
|
||||
if err := netlink.LinkSetDown(sbox); err != nil {
|
||||
return fmt.Errorf("could not set link down for container interface %s: %v", containerIfName, err)
|
||||
}
|
||||
|
||||
// Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP.
|
||||
mac := electMacAddress(epConfig, ip4)
|
||||
err = netlink.LinkSetHardwareAddr(sbox, mac)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err)
|
||||
}
|
||||
endpoint.macAddress = mac
|
||||
|
||||
// Up the host interface after finishing all netlink configuration
|
||||
if err := netlink.LinkSetUp(host); err != nil {
|
||||
return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
|
||||
}
|
||||
|
||||
// v6 address for the sandbox side pipe interface
|
||||
ipv6Addr = &net.IPNet{}
|
||||
if config.EnableIPv6 {
|
||||
@@ -571,7 +596,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
|
||||
|
||||
// Create the sandbox side pipe interface
|
||||
intf := &sandbox.Interface{}
|
||||
intf.SrcName = name2
|
||||
intf.SrcName = containerIfName
|
||||
intf.DstName = containerVethPrefix
|
||||
intf.Address = ipv4Addr
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
bri "github.com/docker/libcontainer/netlink"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// SetupDevice create a new bridge interface/
|
||||
func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
var setMac bool
|
||||
|
||||
// We only attempt to create the bridge when the requested device name is
|
||||
// the default one.
|
||||
if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge {
|
||||
@@ -26,12 +27,10 @@ func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error {
|
||||
// was not supported before that.
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
|
||||
i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC()
|
||||
log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr)
|
||||
setMac = true
|
||||
}
|
||||
|
||||
// Call out to netlink to create the device.
|
||||
return netlink.LinkAdd(i.Link)
|
||||
return bri.CreateBridge(config.BridgeName, setMac)
|
||||
}
|
||||
|
||||
// SetupDeviceUp ups the given bridge interface.
|
||||
|
||||
@@ -40,7 +40,11 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
|
||||
|
||||
// Join method is invoked when a Sandbox is attached to an endpoint.
|
||||
func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
||||
return (jinfo.SetHostsPath("/etc/hosts"))
|
||||
if err := jinfo.SetHostsPath("/etc/hosts"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return jinfo.SetResolvConfPath("/etc/resolv.conf")
|
||||
}
|
||||
|
||||
// Leave method is invoked when a Sandbox detaches from an endpoint.
|
||||
|
||||
@@ -2,6 +2,7 @@ package libnetwork
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
@@ -45,6 +46,9 @@ type Endpoint interface {
|
||||
|
||||
// Delete and detaches this endpoint from the network.
|
||||
Delete() error
|
||||
|
||||
// Retrieve the interfaces' statistics from the sandbox
|
||||
Statistics() (map[string]*sandbox.InterfaceStatistics, error)
|
||||
}
|
||||
|
||||
// EndpointOption is a option setter function type used to pass varios options to Network
|
||||
@@ -402,6 +406,33 @@ func (ep *endpoint) Delete() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (ep *endpoint) Statistics() (map[string]*sandbox.InterfaceStatistics, error) {
|
||||
m := make(map[string]*sandbox.InterfaceStatistics)
|
||||
|
||||
ep.Lock()
|
||||
n := ep.network
|
||||
skey := ep.container.data.SandboxKey
|
||||
ep.Unlock()
|
||||
|
||||
n.Lock()
|
||||
c := n.ctrlr
|
||||
n.Unlock()
|
||||
|
||||
sbox := c.sandboxGet(skey)
|
||||
if sbox == nil {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, i := range sbox.Interfaces() {
|
||||
if m[i.DstName], err = i.Statistics(); err != nil {
|
||||
return m, err
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) buildHostsFiles() error {
|
||||
var extraContent []etchosts.Record
|
||||
|
||||
@@ -567,9 +598,19 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error {
|
||||
return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath)
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
sBytes, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(dst, sBytes, 0644)
|
||||
}
|
||||
|
||||
func (ep *endpoint) setupDNS() error {
|
||||
ep.Lock()
|
||||
container := ep.container
|
||||
joinInfo := ep.joinInfo
|
||||
ep.Unlock()
|
||||
|
||||
if container == nil {
|
||||
@@ -586,6 +627,14 @@ func (ep *endpoint) setupDNS() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if joinInfo.resolvConfPath != "" {
|
||||
if err := copyFile(joinInfo.resolvConfPath, container.config.resolvConfPath); err != nil {
|
||||
return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", joinInfo.resolvConfPath, container.config.resolvConfPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
resolvConf, err := resolvconf.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -817,6 +817,15 @@ func TestEndpointJoin(t *testing.T) {
|
||||
t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key")
|
||||
}
|
||||
|
||||
// Attempt retrieval of endpoint interfaces statistics
|
||||
stats, err := ep.Statistics()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := stats["eth0"]; !ok {
|
||||
t.Fatalf("Did not find eth0 statistics")
|
||||
}
|
||||
|
||||
checkSandbox(t, info)
|
||||
}
|
||||
|
||||
@@ -1119,6 +1128,74 @@ func TestEnableIPv6(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvConfHost(t *testing.T) {
|
||||
if !netutils.IsRunningInContainer() {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
}
|
||||
|
||||
tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888")
|
||||
|
||||
//take a copy of resolv.conf for restoring after test completes
|
||||
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
//cleanup
|
||||
defer func() {
|
||||
if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := createTestNetwork("host", "testnetwork", options.Generic{}, options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep1, err := n.CreateEndpoint("ep1", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resolvConfPath := "/tmp/libnetwork_test/resolv.conf"
|
||||
defer os.Remove(resolvConfPath)
|
||||
|
||||
_, err = ep1.Join(containerID,
|
||||
libnetwork.JoinOptionResolvConfPath(resolvConfPath))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
err = ep1.Leave(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
finfo, err := os.Stat(resolvConfPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmode := (os.FileMode)(0644)
|
||||
if finfo.Mode() != fmode {
|
||||
t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String())
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(resolvConfPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(content, tmpResolvConf) {
|
||||
t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvConf(t *testing.T) {
|
||||
if !netutils.IsRunningInContainer() {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
@@ -276,6 +276,7 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
|
||||
n.Lock()
|
||||
i.DstName = fmt.Sprintf("%s%d", i.DstName, n.nextIfIndex)
|
||||
n.nextIfIndex++
|
||||
path := n.path
|
||||
n.Unlock()
|
||||
|
||||
runtime.LockOSThread()
|
||||
@@ -287,9 +288,9 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
|
||||
}
|
||||
defer origns.Close()
|
||||
|
||||
f, err := os.OpenFile(n.path, os.O_RDONLY, 0)
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed get network namespace %q: %v", n.path, err)
|
||||
return fmt.Errorf("failed get network namespace %q: %v", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
@@ -325,6 +326,8 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
|
||||
return err
|
||||
}
|
||||
|
||||
i.sandboxKey = path
|
||||
|
||||
n.Lock()
|
||||
n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
|
||||
n.Unlock()
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package sandbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
// Sandbox represents a network sandbox, identified by a specific key. It
|
||||
@@ -74,6 +80,9 @@ type Interface struct {
|
||||
|
||||
// IPv6 address for the interface.
|
||||
AddressIPv6 *net.IPNet
|
||||
|
||||
// Parent sandbox's key
|
||||
sandboxKey string
|
||||
}
|
||||
|
||||
// GetCopy returns a copy of this Interface structure
|
||||
@@ -157,3 +166,95 @@ func (s *Info) Equal(o *Info) bool {
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
func nsInvoke(path string, inNsfunc func(callerFD int) error) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
origns, err := netns.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer origns.Close()
|
||||
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed get network namespace %q: %v", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
nsFD := f.Fd()
|
||||
|
||||
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer netns.Set(origns)
|
||||
|
||||
// Invoked after the namespace switch.
|
||||
return inNsfunc(int(origns))
|
||||
}
|
||||
|
||||
// Statistics returns the statistics for this interface
|
||||
func (i *Interface) Statistics() (*InterfaceStatistics, error) {
|
||||
|
||||
s := &InterfaceStatistics{}
|
||||
|
||||
err := nsInvoke(i.sandboxKey, func(callerFD int) error {
|
||||
// For some reason ioutil.ReadFile(netStatsFile) reads the file in
|
||||
// the default netns when this code is invoked from docker.
|
||||
// Executing "cat <netStatsFile>" works as expected.
|
||||
data, err := exec.Command("cat", netStatsFile).Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s: %v", netStatsFile, err)
|
||||
}
|
||||
return scanInterfaceStats(string(data), i.DstName, s)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", i.DstName, i.sandboxKey, err)
|
||||
}
|
||||
|
||||
return s, err
|
||||
}
|
||||
|
||||
// InterfaceStatistics represents the interface's statistics
|
||||
type InterfaceStatistics struct {
|
||||
RxBytes uint64
|
||||
RxPackets uint64
|
||||
RxErrors uint64
|
||||
RxDropped uint64
|
||||
TxBytes uint64
|
||||
TxPackets uint64
|
||||
TxErrors uint64
|
||||
TxDropped uint64
|
||||
}
|
||||
|
||||
func (is *InterfaceStatistics) String() string {
|
||||
return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
|
||||
is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
|
||||
}
|
||||
|
||||
// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore
|
||||
// we cannot gather the statistics from /sys/class/net/<dev>/statistics/<counter> files. Per-netns stats
|
||||
// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relyes on that).
|
||||
const (
|
||||
netStatsFile = "/proc/net/dev"
|
||||
base = "[ ]*%s:([ ]+[0-9]+){16}"
|
||||
)
|
||||
|
||||
func scanInterfaceStats(data, ifName string, i *InterfaceStatistics) error {
|
||||
var (
|
||||
bktStr string
|
||||
bkt uint64
|
||||
)
|
||||
|
||||
regex := fmt.Sprintf(base, ifName)
|
||||
re := regexp.MustCompile(regex)
|
||||
line := re.FindString(data)
|
||||
|
||||
_, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
|
||||
&bktStr, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt,
|
||||
&bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -157,3 +157,29 @@ func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanStatistics(t *testing.T) {
|
||||
data :=
|
||||
"Inter-| Receive | Transmit\n" +
|
||||
" face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" +
|
||||
" eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
|
||||
" wlan0: 7787685 11141 0 0 0 0 0 0 1681390 7220 0 0 0 0 0 0\n" +
|
||||
" lo: 783782 1853 0 0 0 0 0 0 783782 1853 0 0 0 0 0 0\n" +
|
||||
"lxcbr0: 0 0 0 0 0 0 0 0 9006 61 0 0 0 0 0 0\n"
|
||||
|
||||
i := &InterfaceStatistics{}
|
||||
|
||||
if err := scanInterfaceStats(data, "wlan0", i); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i.TxBytes != 1681390 || i.TxPackets != 7220 || i.RxBytes != 7787685 || i.RxPackets != 11141 {
|
||||
t.Fatalf("Error scanning the statistics")
|
||||
}
|
||||
|
||||
if err := scanInterfaceStats(data, "lxcbr0", i); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i.TxBytes != 9006 || i.TxPackets != 61 || i.RxBytes != 0 || i.RxPackets != 0 {
|
||||
t.Fatalf("Error scanning the statistics")
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -43,13 +43,19 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
mybridge := &netlink.Bridge{netlink.LinkAttrs{Name: "foo"}}
|
||||
la := netlink.NewLinkAttrs()
|
||||
la.Name = "foo"
|
||||
mybridge := &netlink.Bridge{la}}
|
||||
_ := netlink.LinkAdd(mybridge)
|
||||
eth1, _ := netlink.LinkByName("eth1")
|
||||
netlink.LinkSetMaster(eth1, mybridge)
|
||||
}
|
||||
|
||||
```
|
||||
Note `NewLinkAttrs` constructor, it sets default values in structure. For now
|
||||
it sets only `TxQLen` to `-1`, so kernel will set default by itself. If you're
|
||||
using simple initialization(`LinkAttrs{Name: "foo"}`) `TxQLen` will be set to
|
||||
`0` unless you specify it like `LinkAttrs{Name: "foo", TxQLen: 1000}`.
|
||||
|
||||
Add a new ip address to loopback:
|
||||
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ type Addr struct {
|
||||
}
|
||||
|
||||
// String returns $ip/$netmask $label
|
||||
func (addr Addr) String() string {
|
||||
return fmt.Sprintf("%s %s", addr.IPNet, addr.Label)
|
||||
func (a Addr) String() string {
|
||||
return fmt.Sprintf("%s %s", a.IPNet, a.Label)
|
||||
}
|
||||
|
||||
// ParseAddr parses the string representation of an address in the
|
||||
|
||||
+16
-2
@@ -81,7 +81,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
|
||||
index = base.Index
|
||||
}
|
||||
|
||||
res := make([]Addr, 0)
|
||||
var res []Addr
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeIfAddrmsg(m)
|
||||
|
||||
@@ -95,11 +95,17 @@ func AddrList(link Link, family int) ([]Addr, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var local, dst *net.IPNet
|
||||
var addr Addr
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.IFA_ADDRESS:
|
||||
addr.IPNet = &net.IPNet{
|
||||
dst = &net.IPNet{
|
||||
IP: attr.Value,
|
||||
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
|
||||
}
|
||||
case syscall.IFA_LOCAL:
|
||||
local = &net.IPNet{
|
||||
IP: attr.Value,
|
||||
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
|
||||
}
|
||||
@@ -107,6 +113,14 @@ func AddrList(link Link, family int) ([]Addr, error) {
|
||||
addr.Label = string(attr.Value[:len(attr.Value)-1])
|
||||
}
|
||||
}
|
||||
|
||||
// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
|
||||
if local != nil {
|
||||
addr.IPNet = local
|
||||
} else {
|
||||
addr.IPNet = dst
|
||||
}
|
||||
|
||||
res = append(res, addr)
|
||||
}
|
||||
|
||||
|
||||
+28
-3
@@ -10,16 +10,29 @@ type Link interface {
|
||||
Type() string
|
||||
}
|
||||
|
||||
type (
|
||||
NsPid int
|
||||
NsFd int
|
||||
)
|
||||
|
||||
// LinkAttrs represents data shared by most link types
|
||||
type LinkAttrs struct {
|
||||
Index int
|
||||
MTU int
|
||||
TxQLen uint32 // Transmit Queue Length
|
||||
TxQLen int // Transmit Queue Length
|
||||
Name string
|
||||
HardwareAddr net.HardwareAddr
|
||||
Flags net.Flags
|
||||
ParentIndex int // index of the parent link device
|
||||
MasterIndex int // must be the index of a bridge
|
||||
ParentIndex int // index of the parent link device
|
||||
MasterIndex int // must be the index of a bridge
|
||||
Namespace interface{} // nil | NsPid | NsFd
|
||||
}
|
||||
|
||||
// NewLinkAttrs returns LinkAttrs structure filled with default values
|
||||
func NewLinkAttrs() LinkAttrs {
|
||||
return LinkAttrs{
|
||||
TxQLen: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Device links cannot be created via netlink. These links
|
||||
@@ -76,9 +89,21 @@ func (vlan *Vlan) Type() string {
|
||||
return "vlan"
|
||||
}
|
||||
|
||||
type MacvlanMode uint16
|
||||
|
||||
const (
|
||||
MACVLAN_MODE_DEFAULT MacvlanMode = iota
|
||||
MACVLAN_MODE_PRIVATE
|
||||
MACVLAN_MODE_VEPA
|
||||
MACVLAN_MODE_BRIDGE
|
||||
MACVLAN_MODE_PASSTHRU
|
||||
MACVLAN_MODE_SOURCE
|
||||
)
|
||||
|
||||
// Macvlan links have ParentIndex set in their Attrs()
|
||||
type Macvlan struct {
|
||||
LinkAttrs
|
||||
Mode MacvlanMode
|
||||
}
|
||||
|
||||
func (macvlan *Macvlan) Attrs() *LinkAttrs {
|
||||
|
||||
+73
-15
@@ -13,6 +13,15 @@ import (
|
||||
var native = nl.NativeEndian()
|
||||
var lookupByDump = false
|
||||
|
||||
var macvlanModes = [...]uint32{
|
||||
0,
|
||||
nl.MACVLAN_MODE_PRIVATE,
|
||||
nl.MACVLAN_MODE_VEPA,
|
||||
nl.MACVLAN_MODE_BRIDGE,
|
||||
nl.MACVLAN_MODE_PASSTHRU,
|
||||
nl.MACVLAN_MODE_SOURCE,
|
||||
}
|
||||
|
||||
func ensureIndex(link *LinkAttrs) {
|
||||
if link != nil && link.Index == 0 {
|
||||
newlink, _ := LinkByName(link.Name)
|
||||
@@ -39,7 +48,7 @@ func LinkSetUp(link Link) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetUp disables link device.
|
||||
// LinkSetDown disables link device.
|
||||
// Equivalent to: `ip link set $link down`
|
||||
func LinkSetDown(link Link) error {
|
||||
base := link.Attrs()
|
||||
@@ -67,7 +76,7 @@ func LinkSetMTU(link Link, mtu int) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = syscall.IFLA_MTU
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
@@ -91,7 +100,7 @@ func LinkSetName(link Link, name string) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = syscall.IFLA_IFNAME
|
||||
req.AddData(msg)
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name))
|
||||
@@ -112,7 +121,7 @@ func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = syscall.IFLA_ADDRESS
|
||||
req.AddData(msg)
|
||||
|
||||
data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr))
|
||||
@@ -145,7 +154,7 @@ func LinkSetMasterByIndex(link Link, masterIndex int) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = syscall.IFLA_MASTER
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
@@ -170,7 +179,7 @@ func LinkSetNsPid(link Link, nspid int) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = syscall.IFLA_NET_NS_PID
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
@@ -183,7 +192,7 @@ func LinkSetNsPid(link Link, nspid int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// LinkSetNsPid puts the device into a new network namespace. The
|
||||
// LinkSetNsFd puts the device into a new network namespace. The
|
||||
// fd must be an open file descriptor to a network namespace.
|
||||
// Similar to: `ip link set $link netns $ns`
|
||||
func LinkSetNsFd(link Link, fd int) error {
|
||||
@@ -195,7 +204,7 @@ func LinkSetNsFd(link Link, fd int) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = nl.IFLA_NET_NS_FD
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, 4)
|
||||
@@ -312,11 +321,28 @@ func LinkAdd(link Link) error {
|
||||
req.AddData(mtu)
|
||||
}
|
||||
|
||||
if base.TxQLen >= 0 {
|
||||
qlen := nl.NewRtAttr(syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
|
||||
req.AddData(qlen)
|
||||
}
|
||||
|
||||
if base.Namespace != nil {
|
||||
var attr *nl.RtAttr
|
||||
switch base.Namespace.(type) {
|
||||
case NsPid:
|
||||
val := nl.Uint32Attr(uint32(base.Namespace.(NsPid)))
|
||||
attr = nl.NewRtAttr(syscall.IFLA_NET_NS_PID, val)
|
||||
case NsFd:
|
||||
val := nl.Uint32Attr(uint32(base.Namespace.(NsFd)))
|
||||
attr = nl.NewRtAttr(nl.IFLA_NET_NS_FD, val)
|
||||
}
|
||||
|
||||
req.AddData(attr)
|
||||
}
|
||||
|
||||
linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type()))
|
||||
|
||||
nl.NewRtAttrChild(linkInfo, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
|
||||
|
||||
if vlan, ok := link.(*Vlan); ok {
|
||||
b := make([]byte, 2)
|
||||
native.PutUint16(b, uint16(vlan.VlanId))
|
||||
@@ -327,15 +353,23 @@ func LinkAdd(link Link) error {
|
||||
peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil)
|
||||
nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC)
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName))
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
|
||||
if base.TxQLen >= 0 {
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
|
||||
}
|
||||
if base.MTU > 0 {
|
||||
nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
|
||||
}
|
||||
|
||||
} else if vxlan, ok := link.(*Vxlan); ok {
|
||||
addVxlanAttrs(vxlan, linkInfo)
|
||||
} else if ipv, ok := link.(*IPVlan); ok {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
|
||||
} else if macv, ok := link.(*Macvlan); ok {
|
||||
if macv.Mode != MACVLAN_MODE_DEFAULT {
|
||||
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
|
||||
nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode]))
|
||||
}
|
||||
}
|
||||
|
||||
req.AddData(linkInfo)
|
||||
@@ -483,6 +517,8 @@ func linkDeserialize(m []byte) (Link, error) {
|
||||
link = &Vxlan{}
|
||||
case "ipvlan":
|
||||
link = &IPVlan{}
|
||||
case "macvlan":
|
||||
link = &Macvlan{}
|
||||
default:
|
||||
link = &Generic{LinkType: linkType}
|
||||
}
|
||||
@@ -498,6 +534,8 @@ func linkDeserialize(m []byte) (Link, error) {
|
||||
parseVxlanData(link, data)
|
||||
case "ipvlan":
|
||||
parseIPVlanData(link, data)
|
||||
case "macvlan":
|
||||
parseMacvlanData(link, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,7 +558,7 @@ func linkDeserialize(m []byte) (Link, error) {
|
||||
case syscall.IFLA_MASTER:
|
||||
base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
|
||||
case syscall.IFLA_TXQLEN:
|
||||
base.TxQLen = native.Uint32(attr.Value[0:4])
|
||||
base.TxQLen = int(native.Uint32(attr.Value[0:4]))
|
||||
}
|
||||
}
|
||||
// Links that don't have IFLA_INFO_KIND are hardware devices
|
||||
@@ -547,8 +585,7 @@ func LinkList() ([]Link, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]Link, 0)
|
||||
|
||||
var res []Link
|
||||
for _, m := range msgs {
|
||||
link, err := linkDeserialize(m)
|
||||
if err != nil {
|
||||
@@ -593,7 +630,7 @@ func setProtinfoAttr(link Link, mode bool, attr int) error {
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(base.Index)
|
||||
msg.Change = nl.DEFAULT_CHANGE
|
||||
msg.Change = syscall.IFLA_PROTINFO | syscall.NLA_F_NESTED
|
||||
req.AddData(msg)
|
||||
|
||||
br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
|
||||
@@ -674,6 +711,27 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
}
|
||||
}
|
||||
|
||||
func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
|
||||
macv := link.(*Macvlan)
|
||||
for _, datum := range data {
|
||||
if datum.Attr.Type == nl.IFLA_MACVLAN_MODE {
|
||||
switch native.Uint32(datum.Value[0:4]) {
|
||||
case nl.MACVLAN_MODE_PRIVATE:
|
||||
macv.Mode = MACVLAN_MODE_PRIVATE
|
||||
case nl.MACVLAN_MODE_VEPA:
|
||||
macv.Mode = MACVLAN_MODE_VEPA
|
||||
case nl.MACVLAN_MODE_BRIDGE:
|
||||
macv.Mode = MACVLAN_MODE_BRIDGE
|
||||
case nl.MACVLAN_MODE_PASSTHRU:
|
||||
macv.Mode = MACVLAN_MODE_PASSTHRU
|
||||
case nl.MACVLAN_MODE_SOURCE:
|
||||
macv.Mode = MACVLAN_MODE_SOURCE
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copied from pkg/net_linux.go
|
||||
func linkFlags(rawFlags uint32) net.Flags {
|
||||
var f net.Flags
|
||||
|
||||
+114
-5
@@ -8,7 +8,10 @@ import (
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
const testTxQLen uint32 = 100
|
||||
const (
|
||||
testTxQLen int = 100
|
||||
defaultTxQLen int = 1000
|
||||
)
|
||||
|
||||
func testLinkAddDel(t *testing.T, link Link) {
|
||||
links, err := LinkList()
|
||||
@@ -50,9 +53,9 @@ func testLinkAddDel(t *testing.T, link Link) {
|
||||
}
|
||||
}
|
||||
|
||||
if veth, ok := link.(*Veth); ok {
|
||||
if veth.TxQLen != testTxQLen {
|
||||
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, testTxQLen)
|
||||
if veth, ok := result.(*Veth); ok {
|
||||
if rBase.TxQLen != base.TxQLen {
|
||||
t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen)
|
||||
}
|
||||
if rBase.MTU != base.MTU {
|
||||
t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU)
|
||||
@@ -91,6 +94,16 @@ func testLinkAddDel(t *testing.T, link Link) {
|
||||
}
|
||||
}
|
||||
|
||||
if macv, ok := link.(*Macvlan); ok {
|
||||
other, ok := result.(*Macvlan)
|
||||
if !ok {
|
||||
t.Fatal("Result of create is not a macvlan")
|
||||
}
|
||||
if macv.Mode != other.Mode {
|
||||
t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
if err = LinkDel(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -199,7 +212,10 @@ func TestLinkAddDelMacvlan(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}})
|
||||
testLinkAddDel(t, &Macvlan{
|
||||
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
|
||||
Mode: MACVLAN_MODE_PRIVATE,
|
||||
})
|
||||
|
||||
if err := LinkDel(parent); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -213,6 +229,99 @@ func TestLinkAddDelVeth(t *testing.T) {
|
||||
testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"})
|
||||
}
|
||||
|
||||
func TestLinkAddVethWithDefaultTxQLen(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
la := NewLinkAttrs()
|
||||
la.Name = "foo"
|
||||
|
||||
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
|
||||
if err := LinkAdd(veth); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link, err := LinkByName("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if veth, ok := link.(*Veth); !ok {
|
||||
t.Fatalf("unexpected link type: %T", link)
|
||||
} else {
|
||||
if veth.TxQLen != defaultTxQLen {
|
||||
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
|
||||
}
|
||||
}
|
||||
peer, err := LinkByName("bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if veth, ok := peer.(*Veth); !ok {
|
||||
t.Fatalf("unexpected link type: %T", link)
|
||||
} else {
|
||||
if veth.TxQLen != defaultTxQLen {
|
||||
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkAddVethWithZeroTxQLen(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
la := NewLinkAttrs()
|
||||
la.Name = "foo"
|
||||
la.TxQLen = 0
|
||||
|
||||
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
|
||||
if err := LinkAdd(veth); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link, err := LinkByName("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if veth, ok := link.(*Veth); !ok {
|
||||
t.Fatalf("unexpected link type: %T", link)
|
||||
} else {
|
||||
if veth.TxQLen != 0 {
|
||||
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
|
||||
}
|
||||
}
|
||||
peer, err := LinkByName("bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if veth, ok := peer.(*Veth); !ok {
|
||||
t.Fatalf("unexpected link type: %T", link)
|
||||
} else {
|
||||
if veth.TxQLen != 0 {
|
||||
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkAddDummyWithTxQLen(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
la := NewLinkAttrs()
|
||||
la.Name = "foo"
|
||||
la.TxQLen = 1500
|
||||
|
||||
dummy := &Dummy{LinkAttrs: la}
|
||||
if err := LinkAdd(dummy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
link, err := LinkByName("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if dummy, ok := link.(*Dummy); !ok {
|
||||
t.Fatalf("unexpected link type: %T", link)
|
||||
} else {
|
||||
if dummy.TxQLen != 1500 {
|
||||
t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkAddDelBridgeMaster(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
||||
@@ -141,7 +141,7 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]Neigh, 0)
|
||||
var res []Neigh
|
||||
for _, m := range msgs {
|
||||
ndm := deserializeNdmsg(m)
|
||||
if linkIndex != 0 && int(ndm.Index) != linkIndex {
|
||||
|
||||
@@ -79,3 +79,18 @@ const (
|
||||
// not defined in syscall
|
||||
IFLA_NET_NS_FD = 28
|
||||
)
|
||||
|
||||
const (
|
||||
IFLA_MACVLAN_UNSPEC = iota
|
||||
IFLA_MACVLAN_MODE
|
||||
IFLA_MACVLAN_FLAGS
|
||||
IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS
|
||||
)
|
||||
|
||||
const (
|
||||
MACVLAN_MODE_PRIVATE = 1
|
||||
MACVLAN_MODE_VEPA = 2
|
||||
MACVLAN_MODE_BRIDGE = 4
|
||||
MACVLAN_MODE_PASSTHRU = 8
|
||||
MACVLAN_MODE_SOURCE = 16
|
||||
)
|
||||
|
||||
+11
-11
@@ -172,16 +172,16 @@ type NetlinkRequest struct {
|
||||
}
|
||||
|
||||
// Serialize the Netlink Request into a byte array
|
||||
func (msg *NetlinkRequest) Serialize() []byte {
|
||||
func (req *NetlinkRequest) Serialize() []byte {
|
||||
length := syscall.SizeofNlMsghdr
|
||||
dataBytes := make([][]byte, len(msg.Data))
|
||||
for i, data := range msg.Data {
|
||||
dataBytes := make([][]byte, len(req.Data))
|
||||
for i, data := range req.Data {
|
||||
dataBytes[i] = data.Serialize()
|
||||
length = length + len(dataBytes[i])
|
||||
}
|
||||
msg.Len = uint32(length)
|
||||
req.Len = uint32(length)
|
||||
b := make([]byte, length)
|
||||
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(msg)))[:]
|
||||
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
|
||||
next := syscall.SizeofNlMsghdr
|
||||
copy(b[0:next], hdr)
|
||||
for _, data := range dataBytes {
|
||||
@@ -193,9 +193,9 @@ func (msg *NetlinkRequest) Serialize() []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
func (msg *NetlinkRequest) AddData(data NetlinkRequestData) {
|
||||
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
|
||||
if data != nil {
|
||||
msg.Data = append(msg.Data, data)
|
||||
req.Data = append(req.Data, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,11 +218,11 @@ func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([][]byte, 0)
|
||||
var res [][]byte
|
||||
|
||||
done:
|
||||
for {
|
||||
msgs, err := s.Recieve()
|
||||
msgs, err := s.Receive()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -294,7 +294,7 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
|
||||
|
||||
// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
|
||||
// and subscribe it to multicast groups passed in variable argument list.
|
||||
// Returns the netlink socket on whic hReceive() method can be called
|
||||
// Returns the netlink socket on which Receive() method can be called
|
||||
// to retrieve the messages from the kernel.
|
||||
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
|
||||
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
|
||||
@@ -329,7 +329,7 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
|
||||
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
|
||||
rb := make([]byte, syscall.Getpagesize())
|
||||
nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
|
||||
if err != nil {
|
||||
|
||||
@@ -104,9 +104,8 @@ func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet {
|
||||
ip := x.ToIP()
|
||||
if GetIPFamily(ip) == FAMILY_V4 {
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)}
|
||||
} else {
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
|
||||
}
|
||||
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
|
||||
}
|
||||
|
||||
func (x *XfrmAddress) FromIP(ip net.IP) {
|
||||
@@ -125,8 +124,8 @@ func DeserializeXfrmAddress(b []byte) *XfrmAddress {
|
||||
return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0]))
|
||||
}
|
||||
|
||||
func (msg *XfrmAddress) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(msg)))[:]
|
||||
func (x *XfrmAddress) Serialize() []byte {
|
||||
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(x)))[:]
|
||||
}
|
||||
|
||||
// struct xfrm_selector {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ type Protinfo struct {
|
||||
|
||||
// String returns a list of enabled flags
|
||||
func (prot *Protinfo) String() string {
|
||||
boolStrings := make([]string, 0)
|
||||
var boolStrings []string
|
||||
if prot.Hairpin {
|
||||
boolStrings = append(boolStrings, "Hairpin")
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func RouteList(link Link, family int) ([]Route, error) {
|
||||
}
|
||||
|
||||
native := nl.NativeEndian()
|
||||
res := make([]Route, 0)
|
||||
var res []Route
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeRtMsg(m)
|
||||
|
||||
@@ -193,7 +193,7 @@ func RouteGet(destination net.IP) ([]Route, error) {
|
||||
}
|
||||
|
||||
native := nl.NativeEndian()
|
||||
res := make([]Route, 0)
|
||||
var res []Route
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeRtMsg(m)
|
||||
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
|
||||
|
||||
@@ -84,7 +84,7 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]XfrmPolicy, 0)
|
||||
var res []XfrmPolicy
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeXfrmUserpolicyInfo(m)
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ func XfrmStateList(family int) ([]XfrmState, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]XfrmState, 0)
|
||||
var res []XfrmState
|
||||
for _, m := range msgs {
|
||||
msg := nl.DeserializeXfrmUsersaInfo(m)
|
||||
|
||||
|
||||
+12
-15
@@ -52,33 +52,30 @@ func Get() (NsHandle, error) {
|
||||
return GetFromThread(os.Getpid(), syscall.Gettid())
|
||||
}
|
||||
|
||||
// GetFromName gets a handle to a named network namespace such as one
|
||||
// created by `ip netns add`.
|
||||
func GetFromName(name string) (NsHandle, error) {
|
||||
fd, err := syscall.Open(fmt.Sprintf("/var/run/netns/%s", name), syscall.O_RDONLY, 0)
|
||||
// GetFromPath gets a handle to a network namespace
|
||||
// identified by the path
|
||||
func GetFromPath(path string) (NsHandle, error) {
|
||||
fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return NsHandle(fd), nil
|
||||
}
|
||||
|
||||
// GetFromName gets a handle to a named network namespace such as one
|
||||
// created by `ip netns add`.
|
||||
func GetFromName(name string) (NsHandle, error) {
|
||||
return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name))
|
||||
}
|
||||
|
||||
// GetFromPid gets a handle to the network namespace of a given pid.
|
||||
func GetFromPid(pid int) (NsHandle, error) {
|
||||
fd, err := syscall.Open(fmt.Sprintf("/proc/%d/ns/net", pid), syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return NsHandle(fd), nil
|
||||
return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid))
|
||||
}
|
||||
|
||||
// GetFromThread gets a handle to the network namespace of a given pid and tid.
|
||||
func GetFromThread(pid, tid int) (NsHandle, error) {
|
||||
name := fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)
|
||||
fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return NsHandle(fd), nil
|
||||
return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid))
|
||||
}
|
||||
|
||||
// GetFromDocker gets a handle to the network namespace of a docker container.
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 374
|
||||
SYS_SETNS = 375
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user