--- a/components/docker/patches/0001-Solaris-v1.10.3.patch Thu Oct 06 14:41:19 2016 -0700
+++ b/components/docker/patches/0001-Solaris-v1.10.3.patch Thu Oct 06 16:05:56 2016 -0700
@@ -1,18 +1,13 @@
-In-house patch which contains a full port of the v1.10.3 Docker
-Engine for Solaris. This is being integrated as the first
-version of Docker on Solaris, targeting release with Solaris 12.
-
-While work is ongoing upstream in the public project, and
-most if not all of that code will be integrated upstream, that
-work will not be done in time to target a Solaris 12 release.
-This version is the first in hopefully many, and this patch
-will be deprecated in future release integrations.
+From 3cf1310ac1de1c3ed1be6341d5d8e6afe255c89f Mon Sep 17 00:00:00 2001
+From: Amit Krishnan <[email protected]>
+Date: Thu, 6 Oct 2016 14:24:52 -0700
Subject: [PATCH] Solaris-v1.10.3
---
Dockerfile.solaris | 26 +
Makefile | 35 +-
api/client/run.go | 1 +
+ api/client/version.go | 20 -
api/server/router/container/container_routes.go | 7 +
api/server/server_unix.go | 2 +-
container/container_solaris.go | 649 ++++++++++++
@@ -27,7 +22,7 @@
daemon/config_solaris.go | 66 ++
daemon/config_test.go | 30 +-
daemon/container_operations_solaris.go | 973 ++++++++++++++++++
- daemon/daemon.go | 9 +-
+ daemon/daemon.go | 6 +-
daemon/daemon_solaris.go | 544 ++++++++++
daemon/daemon_test.go | 2 +
daemon/daemon_unix_test.go | 2 +-
@@ -38,7 +33,7 @@
daemon/execdriver/driver_solaris.go | 76 ++
daemon/execdriver/driver_unix.go | 2 +-
.../execdriver/execdrivers/execdrivers_solaris.go | 13 +
- daemon/execdriver/zones/driver.go | 772 ++++++++++++++
+ daemon/execdriver/zones/driver.go | 745 ++++++++++++++
daemon/execdriver/zones/driver_unsupported.go | 12 +
.../execdriver/zones/driver_unsupported_nocgo.go | 13 +
daemon/graphdriver/driver_solaris.go | 8 +
@@ -50,15 +45,16 @@
daemon/graphdriver/zfs/zfs_linux.go | 37 +
daemon/graphdriver/zfs/zfs_solaris.go | 95 ++
daemon/graphdriver/zfs/zfs_unsupported.go | 2 +-
+ daemon/info.go | 2 -
daemon/inspect_solaris.go | 30 +
daemon/inspect_unix.go | 2 +-
daemon/list_unix.go | 2 +-
daemon/network.go | 7 +
daemon/selinux_unsupported.go | 8 +
- daemon/start.go | 63 ++
- daemon/stats_collector_solaris.go | 139 +++
+ daemon/start.go | 65 ++
+ daemon/stats_collector_solaris.go | 537 ++++++++++
daemon/stats_collector_unix.go | 2 +-
- daemon/stats_solaris.go | 82 ++
+ daemon/stats_solaris.go | 84 ++
docker/daemon_solaris.go | 58 ++
docker/daemon_unix.go | 2 +-
hack/.vendor-helpers.sh | 8 +-
@@ -128,7 +124,7 @@
pkg/signal/signal_solaris.go | 42 +
pkg/signal/signal_unsupported.go | 2 +-
pkg/sysinfo/sysinfo_solaris.go | 117 +++
- pkg/system/meminfo_solaris.go | 133 +++
+ pkg/system/meminfo_solaris.go | 127 +++
pkg/system/meminfo_unsupported.go | 2 +-
pkg/system/stat_linux.go | 33 -
pkg/system/stat_solaris.go | 20 +-
@@ -148,12 +144,13 @@
.../github.com/Sirupsen/logrus/terminal_solaris.go | 15 +
.../docker/engine-api/types/container/config.go | 4 +
.../engine-api/types/container/host_config.go | 1 +
+ .../github.com/docker/engine-api/types/types.go | 2 -
.../docker/go-connections/sockets/unix_socket.go | 2 +-
.../docker/libnetwork/default_gateway_solaris.go | 7 +
- .../libnetwork/drivers/solaris/bridge/bridge.go | 1062 ++++++++++++++++++++
+ .../libnetwork/drivers/solaris/bridge/bridge.go | 1076 ++++++++++++++++++++
.../drivers/solaris/bridge/bridge_store.go | 212 ++++
.../libnetwork/drivers/solaris/bridge/errors.go | 341 +++++++
- .../drivers/solaris/bridge/port_mapping.go | 199 ++++
+ .../drivers/solaris/bridge/port_mapping.go | 218 ++++
.../docker/libnetwork/drivers_solaris.go | 13 +
.../docker/libnetwork/ipamutils/utils_solaris.go | 92 ++
vendor/src/github.com/docker/libnetwork/network.go | 2 -
@@ -172,6 +169,7 @@
.../libnetwork/portmapper/mock_proxy_linux.go | 18 +
.../docker/libnetwork/portmapper/proxy.go | 209 ----
.../docker/libnetwork/portmapper/proxy_linux.go | 209 ++++
+ vendor/src/github.com/docker/libnetwork/sandbox.go | 7 +-
.../libnetwork/sandbox_externalkey_solaris.go | 45 +
.../docker/libnetwork/sandbox_externalkey_unix.go | 2 +-
.../src/github.com/godbus/dbus/transport_unix.go | 2 +-
@@ -190,7 +188,7 @@
.../libcontainer/configs/cgroup_unsupported.go | 2 +-
.../runc/libcontainer/configs/device_defaults.go | 4 +-
.../runc/libcontainer/console_solaris.go | 13 +
- .../runc/libcontainer/container_solaris.go | 103 ++
+ .../runc/libcontainer/container_solaris.go | 22 +
.../runc/libcontainer/stats_solaris.go | 8 +
.../runc/libcontainer/system/sysconfig.go | 2 +-
.../runc/libcontainer/zones/stats.go | 86 ++
@@ -199,7 +197,7 @@
vendor/src/gopkg.in/fsnotify.v1/fsnotify.go | 2 +-
volume/local/local_unix.go | 2 +-
volume/store/store_unix.go | 2 +-
- 189 files changed, 8839 insertions(+), 1215 deletions(-)
+ 193 files changed, 9162 insertions(+), 1241 deletions(-)
create mode 100644 Dockerfile.solaris
create mode 100644 container/container_solaris.go
create mode 100644 container/state_solaris.go
@@ -398,6 +396,65 @@
// No Autoremove: Simply retrieve the exit code
if !config.Tty {
// In non-TTY mode, we can't detach, so we must wait for container exit
+diff --git a/api/client/version.go b/api/client/version.go
+index a64deef..3000b1a 100644
+--- a/api/client/version.go
++++ b/api/client/version.go
+@@ -3,7 +3,6 @@ package client
+ import (
+ "runtime"
+ "text/template"
+- "time"
+
+ Cli "github.com/docker/docker/cli"
+ "github.com/docker/docker/dockerversion"
+@@ -16,8 +15,6 @@ var versionTemplate = `Client:
+ Version: {{.Client.Version}}
+ API version: {{.Client.APIVersion}}
+ Go version: {{.Client.GoVersion}}
+- Git commit: {{.Client.GitCommit}}
+- Built: {{.Client.BuildTime}}
+ OS/Arch: {{.Client.Os}}/{{.Client.Arch}}{{if .Client.Experimental}}
+ Experimental: {{.Client.Experimental}}{{end}}{{if .ServerOK}}
+
+@@ -25,8 +22,6 @@ Server:
+ Version: {{.Server.Version}}
+ API version: {{.Server.APIVersion}}
+ Go version: {{.Server.GoVersion}}
+- Git commit: {{.Server.GitCommit}}
+- Built: {{.Server.BuildTime}}
+ OS/Arch: {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
+ Experimental: {{.Server.Experimental}}{{end}}{{end}}`
+
+@@ -58,8 +53,6 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
+ Version: dockerversion.Version,
+ APIVersion: cli.client.ClientVersion(),
+ GoVersion: runtime.Version(),
+- GitCommit: dockerversion.GitCommit,
+- BuildTime: dockerversion.BuildTime,
+ Os: runtime.GOOS,
+ Arch: runtime.GOARCH,
+ Experimental: utils.ExperimentalBuild(),
+@@ -71,19 +64,6 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
+ vd.Server = &serverVersion
+ }
+
+- // first we need to make BuildTime more human friendly
+- t, errTime := time.Parse(time.RFC3339Nano, vd.Client.BuildTime)
+- if errTime == nil {
+- vd.Client.BuildTime = t.Format(time.ANSIC)
+- }
+-
+- if vd.ServerOK() {
+- t, errTime = time.Parse(time.RFC3339Nano, vd.Server.BuildTime)
+- if errTime == nil {
+- vd.Server.BuildTime = t.Format(time.ANSIC)
+- }
+- }
+-
+ if err2 := tmpl.Execute(cli.out, vd); err2 != nil && err == nil {
+ err = err2
+ }
diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go
index 4e2ffca..e58405b 100644
--- a/api/server/router/container/container_routes.go
@@ -2471,7 +2528,7 @@
+ return fmt.Errorf("Container %s is marked for removal and cannot be connected or disconnected to the network", containerID)
+}
diff --git a/daemon/daemon.go b/daemon/daemon.go
-index 6cb7f8c..3d2b2f8 100644
+index 6cb7f8c..f360392 100644
--- a/daemon/daemon.go
+++ b/daemon/daemon.go
@@ -766,7 +766,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
@@ -2497,16 +2554,6 @@
}
// Run uses the execution driver to run a given container
-@@ -1040,6 +1042,9 @@ func (daemon *Daemon) ExportImage(names []string, outStream io.Writer) error {
-
- // PushImage initiates a push operation on the repository named localName.
- func (daemon *Daemon) PushImage(ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
-+ if runtime.GOOS == "solaris" {
-+ return fmt.Errorf("Pushing an image not supported on Solaris platform")
-+ }
- // Include a buffer so that slow client connections don't affect
- // transfer performance.
- progressChan := make(chan progress.Progress, 100)
diff --git a/daemon/daemon_solaris.go b/daemon/daemon_solaris.go
new file mode 100644
index 0000000..ebec5ad
@@ -3244,10 +3291,10 @@
+}
diff --git a/daemon/execdriver/zones/driver.go b/daemon/execdriver/zones/driver.go
new file mode 100644
-index 0000000..b407ba5
+index 0000000..de5b637
--- /dev/null
+++ b/daemon/execdriver/zones/driver.go
-@@ -0,0 +1,772 @@
+@@ -0,0 +1,745 @@
+// +build solaris,cgo
+
+package zones
@@ -3266,11 +3313,9 @@
+ "strings"
+ "sync"
+ "syscall"
-+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/docker/docker/daemon/execdriver"
-+ sysinfo "github.com/docker/docker/pkg/system"
+ "github.com/opencontainers/runc/libcontainer"
+)
+
@@ -3371,7 +3416,6 @@
+type Driver struct {
+ root string
+ activeContainers map[string]*activeContainer
-+ machineMemory int64
+ sync.Mutex
+}
+
@@ -3447,29 +3491,26 @@
+
+func startWrapper(cmd *exec.Cmd) error {
+ // create processes in their own process contracts
-+ var errn, cttmpl C.int
-+
-+ if cttmpl = C.ct_pr_tmpl(); cttmpl == -1 {
-+ errno_msg := C.GoString(C.strerror(errn))
-+ return errors.New("Failed to create process contract template: " + errno_msg)
++ var cttmpl C.int
++ var err error
++
++ if cttmpl, err = C.ct_pr_tmpl(); cttmpl == -1 {
++ return err
+ }
+ defer func() {
-+ if errn = C.ct_abandon_latest(); errn != 0 {
-+ logrus.Error("Failed to abandon process contract: %v", C.GoString(C.strerror(errn)))
-+ } else if errn = C.ct_clear(cttmpl); errn != 0 {
-+ logrus.Error("Failed to clear process contract template %v", C.GoString(C.strerror(errn)))
++ if errn := C.ct_abandon_latest(); errn != 0 {
++ logrus.Error("Failed to abandon process contract: ", C.GoString(C.strerror(errn)))
++ }
++ if errn := C.ct_clear(cttmpl); errn != 0 {
++ logrus.Error("Failed to clear process contract template ", C.GoString(C.strerror(errn)))
+ }
+ }()
-+ err := cmd.Start()
++ err = cmd.Start()
+
+ return err
+}
+
+func NewDriver(root string, options []string) (*Driver, error) {
-+ meminfo, err := sysinfo.ReadMemInfo()
-+ if err != nil {
-+ return nil, err
-+ }
+ if err := os.MkdirAll(root, 0700); err != nil {
+ return nil, err
+ }
@@ -3477,7 +3518,6 @@
+ return &Driver{
+ root: root,
+ activeContainers: make(map[string]*activeContainer),
-+ machineMemory: meminfo.MemTotal,
+ }, nil
+}
+
@@ -3826,7 +3866,6 @@
+ return execdriver.ExitStatus{ExitCode: -1}, err
+ }
+
-+
+ if pipes.Stdin != nil {
+ go func() {
+ io.Copy(w, pipes.Stdin)
@@ -3973,26 +4012,7 @@
+
+// Stats implements the exec driver Driver interface.
+func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
-+ stats, err := libcontainer.GetStats(id)
-+ if err != nil {
-+ return nil, err
-+ }
-+ // XXX: get from container configuration the memory resource limit
-+ // in linux: c.Config().Cgroups.Resources.Memory
-+ var memoryLimit int64 = 0
-+ // if the container does not have any memory limit specified set the
-+ // limit to the machines memory
-+ if memoryLimit == 0 {
-+ memoryLimit = d.machineMemory
-+ }
-+
-+ p := &execdriver.ResourceStats{
-+ Stats: stats,
-+ Read: time.Now(),
-+ MemoryLimit: memoryLimit,
-+ }
-+
-+ return p, nil
++ return nil, errors.New("Stats is not supported in zones execdriver")
+}
+
+// Stats implements the exec driver Driver interface.
@@ -4417,6 +4437,22 @@
package zfs
+diff --git a/daemon/info.go b/daemon/info.go
+index 008ac20..3d86bc0 100644
+--- a/daemon/info.go
++++ b/daemon/info.go
+@@ -134,11 +134,9 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
+ func (daemon *Daemon) SystemVersion() types.Version {
+ v := types.Version{
+ Version: dockerversion.Version,
+- GitCommit: dockerversion.GitCommit,
+ GoVersion: runtime.Version(),
+ Os: runtime.GOOS,
+ Arch: runtime.GOARCH,
+- BuildTime: dockerversion.BuildTime,
+ Experimental: utils.ExperimentalBuild(),
+ }
+
diff --git a/daemon/inspect_solaris.go b/daemon/inspect_solaris.go
new file mode 100644
index 0000000..e42a61d
@@ -4529,7 +4565,7 @@
+ return nil, nil
+}
diff --git a/daemon/start.go b/daemon/start.go
-index 418dace..c74f07a 100644
+index 418dace..9d5e6cb 100644
--- a/daemon/start.go
+++ b/daemon/start.go
@@ -1,7 +1,10 @@
@@ -4552,18 +4588,7 @@
// ContainerStart starts a container.
func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig) error {
container, err := daemon.GetContainer(name)
-@@ -109,6 +114,10 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error)
- return err
- }
-
-+ if err := daemon.injectHostConfig(container); err != nil {
-+ return err
-+ }
-+
- // Make sure NetworkMode has an acceptable value. We do this to ensure
- // backwards API compatibility.
- container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
-@@ -142,6 +151,18 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error)
+@@ -142,6 +147,24 @@ func (daemon *Daemon) containerStart(container *container.Container) (err error)
mounts = append(mounts, container.TmpfsMounts()...)
container.Command.Mounts = mounts
@@ -4579,10 +4604,16 @@
+ container.Command.ContOS = img.Os
+ }
+
++ if container.Command.ContOS == "solaris" {
++ if err := daemon.injectHostConfig(container); err != nil {
++ return err
++ }
++
++ }
if err := daemon.waitForStart(container); err != nil {
return err
}
-@@ -170,3 +191,45 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
+@@ -170,3 +193,45 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
}
}
@@ -4630,28 +4661,260 @@
+}
diff --git a/daemon/stats_collector_solaris.go b/daemon/stats_collector_solaris.go
new file mode 100644
-index 0000000..9a51b27
+index 0000000..4d4b4a0
--- /dev/null
+++ b/daemon/stats_collector_solaris.go
-@@ -0,0 +1,139 @@
+@@ -0,0 +1,537 @@
+package daemon
+
+import (
+ "bufio"
-+ "sync"
-+ "time"
+ "github.com/Sirupsen/logrus"
+ "github.com/docker/docker/container"
+ "github.com/docker/docker/daemon/execdriver"
+ "github.com/docker/docker/pkg/pubsub"
++ "github.com/docker/docker/pkg/system"
++ "github.com/docker/libnetwork/types"
++ "github.com/opencontainers/runc/libcontainer"
++ "github.com/opencontainers/runc/libcontainer/zones"
++ "strings"
++ "sync"
++ "time"
+)
+
++/*
++
++#cgo LDFLAGS: -lzonestat -lkstat2
++
++#include <errno.h>
++#include <kstat2.h>
++#include <string.h>
++#include <strings.h>
++#include <stdlib.h>
++#include <zone.h>
++#include <zonestat.h>
++#include <sys/param.h>
++
++typedef struct z_iostat {
++ int64_t nread;
++ int64_t read_bytes;
++ int64_t nwrite;
++ int64_t write_bytes;
++} z_iostat_t;
++
++typedef struct z_netstat {
++ int64_t ibytes;
++ int64_t ipackets;
++ int64_t ierrors;
++ int64_t idrops;
++ int64_t obytes;
++ int64_t opackets;
++ int64_t oerrors;
++ int64_t odrops;
++} z_netstat_t;
++
++#define KSTAT2_IO_URI "kstat:/misc/unix/vopstats_zfs/%d"
++z_iostat_t *
++get_io_stats(zoneid_t zoneid) {
++ kstat2_handle_t handle;
++ kstat2_status_t stat;
++ kstat2_map_t map;
++ kstat2_mapiter_t iter;
++ boolean_t has_next;
++ z_iostat_t *zio = malloc(sizeof(*zio));
++
++ if (zio == NULL) {
++ errno = ENOMEM;
++ return (NULL);
++ }
++ char lookup_uri[sizeof(KSTAT2_IO_URI) + 10]; // 12 extra digits
++
++ (void) snprintf(lookup_uri, sizeof(lookup_uri), KSTAT2_IO_URI, zoneid);
++
++ stat = kstat2_open(&handle, NULL);
++ if (stat != KSTAT2_S_OK) {
++ return (NULL);
++ }
++
++ stat = kstat2_lookup_map(handle, lookup_uri, &map);
++ if (stat != KSTAT2_S_OK) {
++ (void) kstat2_close(&handle);
++ return (NULL);
++ }
++
++ if ((stat = kstat2_mapiter_start(map, KSTAT2_NVK_ALL, &iter)) !=
++ KSTAT2_S_OK) {
++ (void) kstat2_close(&handle);
++ return (NULL);
++ }
++
++ while (kstat2_mapiter_hasnext(iter, &has_next) == KSTAT2_S_OK &&
++ has_next) {
++ kstat2_nv_t val;
++
++ if (kstat2_mapiter_next(iter, &val) != KSTAT2_S_OK) {
++ continue;
++ }
++
++ if (!strcmp(val->name, "nread") && (val->type == KSTAT2_NVVT_INT))
++ zio->nread = val->data.integer;
++ else if (!strcmp(val->name, "read_bytes") && (val->type == KSTAT2_NVVT_INT))
++ zio->read_bytes = val->data.integer;
++ else if (!strcmp(val->name, "nwrite") && (val->type == KSTAT2_NVVT_INT))
++ zio->nwrite = val->data.integer;
++ else if (!strcmp(val->name, "write_bytes") && (val->type == KSTAT2_NVVT_INT))
++ zio->write_bytes = val->data.integer;
++ }
++
++ (void) kstat2_close(&handle);
++ return (zio);
++}
++
++void
++io_stats_free(z_iostat_t *p)
++{
++ free(p);
++}
++
++#define KSTAT2_NET_LINK_URI "kstat:/net/link"
++
++z_netstat_t *
++get_net_stats(const char *linkname, zoneid_t zoneid) {
++ kstat2_handle_t handle;
++ kstat2_status_t stat;
++ kstat2_map_t map;
++ kstat2_mapiter_t iter;
++ boolean_t has_next;
++ // URI prefix + '/' + max data link name + '/' + zoneid digits + '\0'
++ char lookup_uri[sizeof(KSTAT2_NET_LINK_URI) + MAXLINKNAMELEN + 12];
++ z_netstat_t *zns = calloc(1, sizeof(*zns));
++
++ if (zns == NULL) {
++ errno = ENOMEM;
++ return (NULL);
++ }
++
++ (void) snprintf(lookup_uri, sizeof(lookup_uri), "%s/%s/%d",
++ KSTAT2_NET_LINK_URI, linkname, zoneid);
++
++ stat = kstat2_open(&handle, NULL);
++ if (stat != KSTAT2_S_OK) {
++ free(zns);
++ return (NULL);
++ }
++
++ stat = kstat2_lookup_map(handle, lookup_uri, &map);
++ if (stat != KSTAT2_S_OK) {
++ (void) kstat2_close(&handle);
++ free(zns);
++ return (NULL);
++ }
++
++ stat = kstat2_mapiter_start(map, KSTAT2_NVK_ALL, &iter);
++ if (stat != KSTAT2_S_OK) {
++ (void) kstat2_close(&handle);
++ free(zns);
++ return (NULL);
++ }
++
++ while (kstat2_mapiter_hasnext(iter, &has_next) == KSTAT2_S_OK &&
++ has_next) {
++ kstat2_nv_t val;
++
++ if (kstat2_mapiter_next(iter, &val) != KSTAT2_S_OK ||
++ val->type != KSTAT2_NVVT_INT) {
++ continue;
++ }
++
++ if (strcmp(val->name, "ipackets64") == 0) {
++ zns->ipackets = val->data.integer;
++ } else if (strcmp(val->name, "rbytes64") == 0) {
++ zns->ibytes = val->data.integer;
++ } else if (strcmp(val->name, "ierrors") == 0) {
++ zns->ierrors = val->data.integer;
++ } else if (strcmp(val->name, "dl_idrops") == 0) {
++ zns->idrops = val->data.integer;
++ } else if (strcmp(val->name, "opackets64") == 0) {
++ zns->opackets = val->data.integer;
++ } else if (strcmp(val->name, "obytes64") == 0) {
++ zns->obytes = val->data.integer;
++ } else if (strcmp(val->name, "oerrors") == 0) {
++ zns->oerrors = val->data.integer;
++ } else if (strcmp(val->name, "dl_odrops") == 0) {
++ zns->odrops = val->data.integer;
++ }
++ }
++
++ (void) kstat2_close(&handle);
++ return (zns);
++}
++
++void
++net_stats_free(z_netstat_t *p)
++{
++ free(p);
++}
++
++uint64_t
++getSystemCPUUsage(zs_usage_t usage)
++{
++ timestruc_t cpu;
++
++ zs_resource_total_time(usage, ZS_RESOURCE_CPU, &cpu);
++ return (1000000000ULL * cpu.tv_sec + cpu.tv_nsec);
++}
++
++char *
++get_zone_name(zs_zone_t z)
++{
++ zs_property_t prop;
++
++ prop = zs_zone_property(z, ZS_ZONE_PROP_NAME);
++ if (prop == NULL)
++ return (NULL);
++
++ return (zs_property_string(prop));
++}
++
++zs_usage_t
++get_usage(zs_ctl_t zsctl)
++{
++ zs_usage_t zsu;
++
++ while ((zsu = zs_usage_read(zsctl)) == NULL &&
++ (errno == EINTR || errno == EAGAIN))
++ ;
++
++ return (zsu);
++}
++
++uint64_t
++getCpuUsage(zs_zone_t z)
++{
++ timestruc_t cpu;
++
++ zs_resource_used_zone_time(z, ZS_RESOURCE_CPU, &cpu);
++ return ((uint64_t)(cpu.tv_sec * 1000000000ULL + cpu.tv_nsec));
++}
++
++*/
++import "C"
++
++func getZoneName(z C.zs_zone_t) string {
++ return C.GoString(C.get_zone_name(z))
++}
++
+// XXX solaris: TODO Copied from Windows, refactor accordingly for collector actions.
+// XXX: Copied statsCollector struct and interface from unix
+
+type statsSupervisor interface {
-+ // GetContainerStats collects all the stats related to a container
-+ GetContainerStats(container *container.Container) (*execdriver.ResourceStats, error)
++ // GetZoneStats collects all the stats related to a zone container
++ getZoneStats(z C.zs_zone_t, zoneid uint64, container *container.Container) (*execdriver.ResourceStats, error)
++}
++
++type zonePublisher struct {
++ publisher *pubsub.Publisher
++ zoneid uint64
+}
+
+// newStatsCollector returns a new statsCollector for collection stats
@@ -4660,10 +4923,10 @@
+// they are started.
+func (daemon *Daemon) newStatsCollector(interval time.Duration) *statsCollector {
+ s := &statsCollector{
-+ interval: interval,
-+ supervisor: daemon,
-+ publishers: make(map[*container.Container]*pubsub.Publisher),
-+ bufReader: bufio.NewReaderSize(nil, 128),
++ interval: interval,
++ supervisor: daemon,
++ publishers: make(map[*container.Container]*zonePublisher),
++ bufReader: bufio.NewReaderSize(nil, 128),
+ }
+ go s.run()
+
@@ -4672,25 +4935,34 @@
+
+// statsCollector manages and provides container resource stats
+type statsCollector struct {
-+ m sync.Mutex
-+ supervisor statsSupervisor
-+ interval time.Duration
-+ publishers map[*container.Container]*pubsub.Publisher
-+ bufReader *bufio.Reader
++ m sync.Mutex
++ supervisor statsSupervisor
++ interval time.Duration
++ publishers map[*container.Container]*zonePublisher
++ bufReader *bufio.Reader
+}
+
+// collect registers the container with the collector and adds it to
++
+// the event loop for collection on the specified interval returning
+// a channel for the subscriber to receive on.
+func (s *statsCollector) collect(c *container.Container) chan interface{} {
+ s.m.Lock()
+ defer s.m.Unlock()
-+ publisher, exists := s.publishers[c]
++ zpub, exists := s.publishers[c]
+ if !exists {
-+ publisher = pubsub.NewPublisher(100*time.Millisecond, 1024)
-+ s.publishers[c] = publisher
-+ }
-+ return publisher.Subscribe()
++ zid, err := C.getzoneidbyname(C.CString(strings.TrimPrefix(c.Name, "/")))
++ if err != nil {
++ // Too early to get zoneid, flag and defer
++ zid = 0
++ }
++ zpub = &zonePublisher{
++ publisher: pubsub.NewPublisher(100*time.Millisecond, 1024),
++ zoneid: uint64(zid),
++ }
++ s.publishers[c] = zpub
++ }
++ return zpub.publisher.Subscribe()
+}
+
+// stopCollection closes the channels for all subscribers and removes
@@ -4698,8 +4970,8 @@
+func (s *statsCollector) stopCollection(c *container.Container) {
+ s.m.Lock()
+ defer s.m.Unlock()
-+ if publisher, exists := s.publishers[c]; exists {
-+ publisher.Close()
++ if zpub, exists := s.publishers[c]; exists {
++ zpub.publisher.Close()
+ delete(s.publishers, c)
+ }
+}
@@ -4708,10 +4980,10 @@
+func (s *statsCollector) unsubscribe(c *container.Container, ch chan interface{}) {
+ s.m.Lock()
+ defer s.m.Unlock()
-+ publisher := s.publishers[c]
-+ if publisher != nil {
-+ publisher.Evict(ch)
-+ if publisher.Len() == 0 {
++ zpub, exists := s.publishers[c]
++ if exists {
++ zpub.publisher.Evict(ch)
++ if zpub.publisher.Len() == 0 {
+ delete(s.publishers, c)
+ }
+ }
@@ -4721,57 +4993,214 @@
+func (s *statsCollector) run() {
+ type publishersPair struct {
+ container *container.Container
-+ publisher *pubsub.Publisher
-+ }
++ zpub *zonePublisher
++ }
++ memoryInfo, err := system.ReadMemInfo()
++ machineMemory := memoryInfo.MemTotal
++
+ // we cannot determine the capacity here.
+ // it will grow enough in first iteration
-+ var pairs []publishersPair
-+
++ pairs := make(map[string]publishersPair)
++
++ zsctl := C.zs_open()
++ usage_last, err := C.get_usage(zsctl)
++ if usage_last == nil {
++ logrus.Error("Failed to get usage: ", err)
++ C.zs_close(zsctl)
++ return
++ }
+ for range time.Tick(s.interval) {
-+ // it does not make sense in the first iteration,
-+ // but saves allocations in further iterations
-+ pairs = pairs[:0]
++ var usage_diff C.zs_usage_t
+
+ s.m.Lock()
-+ for container, publisher := range s.publishers {
++ for container, zpub := range s.publishers {
+ // copy pointers here to release the lock ASAP
-+ pairs = append(pairs, publishersPair{container, publisher})
++ pairs[strings.TrimPrefix(container.Name, "/")] = publishersPair{container, zpub}
+ }
+ s.m.Unlock()
+ if len(pairs) == 0 {
+ continue
+ }
+
-+ // XXX: need to implement getSystmCPUUsage()
-+ // XXX: ? whole system usage or how much of container allocated resources used?
-+ // XXX: The output of docker stats seem broken as the CPU % column show changes
-+ // from previous reading, instead of actual usage.
-+ // Thats in api/client/stats.go calculateCPUUsage()
-+ //
-+ systemUsage, err := s.getSystemCPUUsage()
-+ if err != nil {
-+ logrus.Errorf("collecting system cpu usage: %v", err)
++ usage, err := C.get_usage(zsctl)
++ if usage != nil {
++ usage_diff = C.zs_usage_diff(usage_last, usage)
++ }
++ if usage == nil || usage_diff == nil {
++ logrus.Error("Failed to get usage: ", err)
++ // empty the map
++ for name, _ := range pairs {
++ delete(pairs, name)
++ }
+ continue
+ }
-+
-+ for _, pair := range pairs {
-+ stats, err := s.supervisor.GetContainerStats(pair.container)
++ systemUsage := uint64(C.getSystemCPUUsage(usage_diff))
++
++ var z C.zs_zone_t
++ z = nil
++ for z = C.zs_zone_walk(usage_diff, z); z != nil; z = C.zs_zone_walk(usage_diff, z) {
++ name := getZoneName(z)
++ pair, ok := pairs[name]
++ if ok == false {
++ continue
++ }
++ zoneid := pair.zpub.zoneid
++ if zoneid == 0 {
++ zid, err := C.getzoneidbyname(C.CString(name))
++ if err != nil {
++ // zoneid is not available yet
++ delete(pairs, name)
++ continue
++ }
++ pair.zpub.zoneid = uint64(zid)
++ zoneid = pair.zpub.zoneid
++ }
++ stats, err := s.supervisor.getZoneStats(z, zoneid, pair.container)
+ if err != nil {
+ if err != execdriver.ErrNotRunning {
+ logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err)
+ }
++ delete(pairs, name)
+ continue
+ }
++ if stats.MemoryLimit == 0 {
++ stats.MemoryLimit = machineMemory
++ }
+ stats.SystemUsage = systemUsage
+
-+ pair.publisher.Publish(stats)
-+ }
-+ }
-+}
-+
-+// XXX needs to be implemented.
-+func (s *statsCollector) getSystemCPUUsage() (uint64, error) {
-+ return 0, nil
++ pair.zpub.publisher.Publish(stats)
++ delete(pairs, name)
++ }
++ C.zs_usage_free(usage_last)
++ C.zs_usage_free(usage_diff)
++ usage_last = usage
++ }
++ C.zs_close(zsctl)
++}
++
++func getIOStats(zoneid uint64) (*zones.BlkioStats, error) {
++ iostats, err := C.get_io_stats(C.zoneid_t(zoneid))
++ if iostats == nil {
++ return nil, err
++ }
++
++ ioBytes := []zones.BlkioStatEntry{
++ {
++ Op: "write",
++ Value: uint64(iostats.write_bytes),
++ },
++ {
++ Op: "read",
++ Value: uint64(iostats.read_bytes),
++ },
++ }
++ ioCount := []zones.BlkioStatEntry{
++ {
++ Op: "write",
++ Value: uint64(iostats.nwrite),
++ },
++ {
++ Op: "read",
++ Value: uint64(iostats.nread),
++ },
++ }
++ C.io_stats_free(iostats)
++ ioStats := zones.BlkioStats{
++ IoServiceBytesRecursive: ioBytes,
++ IoServicedRecursive: ioCount,
++ }
++
++ return &ioStats, nil
++}
++
++func getNetStats(zoneid uint64) ([]*libcontainer.NetworkInterface, error) {
++ m := make(map[string]*types.InterfaceStatistics)
++ s := types.InterfaceStatistics{}
++
++ // XXX: Should get an array with all data links in the container from its
++ // configuration. For now, net0 suffices. If multi-homed containers are allowed
++ // in the future, this will be required to have accuarte network statistics.
++ datalink := []string{"net0"}
++ for _, linkname := range datalink {
++ ns, err := C.get_net_stats(C.CString(linkname), C.zoneid_t(zoneid))
++ if ns == nil {
++ return nil, err
++ }
++
++ s.RxBytes = uint64(ns.ibytes)
++ s.RxPackets = uint64(ns.ipackets)
++ s.RxErrors = uint64(ns.ierrors)
++ s.RxDropped = uint64(ns.idrops)
++
++ s.TxBytes = uint64(ns.obytes)
++ s.TxPackets = uint64(ns.opackets)
++ s.TxErrors = uint64(ns.oerrors)
++ s.TxDropped = uint64(ns.odrops)
++ m[linkname] = &s
++ C.net_stats_free(ns)
++
++ }
++ // Convert libnetwork nw stats into libcontainer nw stats
++ var list []*libcontainer.NetworkInterface
++ for ifName, ifStats := range m {
++ list = append(list, convertLnNetworkStats(ifName, ifStats))
++ }
++
++ return list, nil
++}
++
++func getContainerStats(z C.zs_zone_t, zoneid uint64) (*libcontainer.Stats, error) {
++ cstats := &libcontainer.Stats{}
++ zstats := zones.Stats{}
++ iostats, err := getIOStats(zoneid)
++ if iostats == nil {
++ return nil, err
++ }
++
++ // For now we only retrieve what's needed by docker stats
++ cpuUsage := uint64(C.getCpuUsage(z))
++ memUsage := uint64(C.zs_resource_used_zone_uint64(z, C.ZS_RESOURCE_RAM_RSS))
++
++ // XXX: Need to use kstat2 for CPU usage
++ // CPU% only seems to work if all these fields are populated.
++ // Once kstat2 are used, these statistics will be filled accurately.
++ zstats.CpuStats.CpuUsage.PercpuUsage = []uint64{cpuUsage}
++ zstats.CpuStats.CpuUsage.TotalUsage = uint64(cpuUsage)
++ zstats.CpuStats.CpuUsage.UsageInKernelmode = uint64(cpuUsage)
++ zstats.CpuStats.CpuUsage.UsageInUsermode = uint64(cpuUsage)
++ zstats.MemoryStats.Usage.Usage = memUsage
++ zstats.BlkioStats = *iostats
++
++ cstats.Stats = &zstats
++
++ return cstats, nil
++}
++
++func (daemon *Daemon) getZoneStats(z C.zs_zone_t, zoneid uint64, container *container.Container) (*execdriver.ResourceStats, error) {
++
++ zstats, err := getContainerStats(z, zoneid)
++ if zstats == nil {
++ return nil, err
++ }
++ netstats, err := getNetStats(zoneid)
++ if netstats == nil {
++ return nil, err
++ }
++
++ memoryLimit := int64(C.zs_zone_limit_uint64(z, C.ZS_LIMIT_RAM_RSS))
++
++ if memoryLimit == C.ZS_LIMIT_NONE {
++ memoryLimit = 0
++ }
++
++ zstats.Interfaces = netstats
++ stats := &execdriver.ResourceStats{
++ Read: time.Now(),
++ MemoryLimit: memoryLimit,
++ Stats: zstats,
++ }
++
++ return stats, nil
+}
diff --git a/daemon/stats_collector_unix.go b/daemon/stats_collector_unix.go
index 2fd368c..ec408c6 100644
@@ -4785,10 +5214,10 @@
diff --git a/daemon/stats_solaris.go b/daemon/stats_solaris.go
new file mode 100644
-index 0000000..1d99f1f
+index 0000000..a1230c6
--- /dev/null
+++ b/daemon/stats_solaris.go
-@@ -0,0 +1,82 @@
+@@ -0,0 +1,84 @@
+package daemon
+
+import (
@@ -4822,38 +5251,40 @@
+ }
+ }
+
-+ if cs != nil {
-+ s.BlkioStats = types.BlkioStats{
-+ IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive),
-+ IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive),
-+ IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive),
-+ IoServiceTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceTimeRecursive),
-+ IoWaitTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoWaitTimeRecursive),
-+ IoMergedRecursive: copyBlkioEntry(cs.BlkioStats.IoMergedRecursive),
-+ IoTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoTimeRecursive),
-+ SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive),
-+ }
-+ cpu := cs.CpuStats
-+ s.CPUStats = types.CPUStats{
-+ CPUUsage: types.CPUUsage{
-+ TotalUsage: cpu.CpuUsage.TotalUsage,
-+ PercpuUsage: cpu.CpuUsage.PercpuUsage,
-+ UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode,
-+ UsageInUsermode: cpu.CpuUsage.UsageInUsermode,
-+ },
-+ ThrottlingData: types.ThrottlingData{
-+ Periods: cpu.ThrottlingData.Periods,
-+ ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods,
-+ ThrottledTime: cpu.ThrottlingData.ThrottledTime,
-+ },
-+ }
-+ mem := cs.MemoryStats
-+ s.MemoryStats = types.MemoryStats{
-+ Usage: mem.Usage.Usage,
-+ MaxUsage: mem.Usage.MaxUsage,
-+ Stats: mem.Stats,
-+ Failcnt: mem.Usage.Failcnt,
-+ }
++ if cs == nil {
++ return s
++ }
++
++ s.BlkioStats = types.BlkioStats{
++ IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive),
++ IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive),
++ IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive),
++ IoServiceTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceTimeRecursive),
++ IoWaitTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoWaitTimeRecursive),
++ IoMergedRecursive: copyBlkioEntry(cs.BlkioStats.IoMergedRecursive),
++ IoTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoTimeRecursive),
++ SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive),
++ }
++ cpu := cs.CpuStats
++ s.CPUStats = types.CPUStats{
++ CPUUsage: types.CPUUsage{
++ TotalUsage: cpu.CpuUsage.TotalUsage,
++ PercpuUsage: cpu.CpuUsage.PercpuUsage,
++ UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode,
++ UsageInUsermode: cpu.CpuUsage.UsageInUsermode,
++ },
++ ThrottlingData: types.ThrottlingData{
++ Periods: cpu.ThrottlingData.Periods,
++ ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods,
++ ThrottledTime: cpu.ThrottlingData.ThrottledTime,
++ },
++ }
++ mem := cs.MemoryStats
++ s.MemoryStats = types.MemoryStats{
++ Usage: mem.Usage.Usage,
++ MaxUsage: mem.Usage.MaxUsage,
++ Stats: mem.Stats,
++ Failcnt: mem.Usage.Failcnt,
+ }
+
+ return s
@@ -8299,10 +8730,10 @@
+}
diff --git a/pkg/system/meminfo_solaris.go b/pkg/system/meminfo_solaris.go
new file mode 100644
-index 0000000..f8c0af3
+index 0000000..bad227f
--- /dev/null
+++ b/pkg/system/meminfo_solaris.go
-@@ -0,0 +1,133 @@
+@@ -0,0 +1,127 @@
+// +build solaris,cgo
+
+package system
@@ -8378,19 +8809,13 @@
+ return int64(pagesize * npages)
+}
+
-+func getFreeMem() int64 {
-+ pagesize := C.sysconf(C._SC_PAGESIZE)
-+ npages := C.sysconf(C._SC_AVPHYS_PAGES)
-+ return int64(pagesize * npages)
-+}
-+
+// ReadMemInfo retrieves memory statistics of the host system and returns a
+// MemInfo type.
+func ReadMemInfo() (*MemInfo, error) {
+
+ ppKernel := C.getPpKernel()
+ MemTotal := getTotalMem()
-+ MemFree := getFreeMem()
++ MemFree := MemTotal - int64(ppKernel)
+ SwapTotal, SwapFree, err := getSysSwap()
+
+ if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 ||
@@ -8400,7 +8825,7 @@
+
+ meminfo := &MemInfo{}
+ // Total memory is total physical memory less than memory locked by kernel
-+ meminfo.MemTotal = MemTotal - int64(ppKernel)
++ meminfo.MemTotal = MemTotal
+ meminfo.MemFree = MemFree
+ meminfo.SwapTotal = SwapTotal
+ meminfo.SwapFree = SwapFree
@@ -8956,6 +9381,24 @@
Resources
+ LimitPriv string // Comma separated list of privileges to limit container to
}
+diff --git a/vendor/src/github.com/docker/engine-api/types/types.go b/vendor/src/github.com/docker/engine-api/types/types.go
+index 64c9981..d42395e 100644
+--- a/vendor/src/github.com/docker/engine-api/types/types.go
++++ b/vendor/src/github.com/docker/engine-api/types/types.go
+@@ -178,13 +178,11 @@ type ContainerProcessList struct {
+ type Version struct {
+ Version string
+ APIVersion string `json:"ApiVersion"`
+- GitCommit string
+ GoVersion string
+ Os string
+ Arch string
+ KernelVersion string `json:",omitempty"`
+ Experimental bool `json:",omitempty"`
+- BuildTime string `json:",omitempty"`
+ }
+
+ // Info contains response of Remote API:
diff --git a/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go b/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go
index c10aced..d162734 100644
--- a/vendor/src/github.com/docker/go-connections/sockets/unix_socket.go
@@ -8981,10 +9424,10 @@
+}
diff --git a/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go
new file mode 100644
-index 0000000..960f8ea
+index 0000000..12c8b69
--- /dev/null
+++ b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go
-@@ -0,0 +1,1062 @@
+@@ -0,0 +1,1076 @@
+package bridge
+
+import (
@@ -9418,16 +9861,29 @@
+ config.BridgeName, config.AddressIPv4)
+
+ networkList := d.getNetworks()
-+ for _, nw := range networkList {
++ for i, nw := range networkList {
+ nw.Lock()
+ nwConfig := nw.config
+ nw.Unlock()
+ if err := nwConfig.Conflicts(config); err != nil {
-+ return types.ForbiddenErrorf(
-+ "cannot create network %s (%s): "+
-+ "conflicts with network %s (%s): %s",
-+ nwConfig.BridgeName, config.ID, nw.id,
-+ nw.config.BridgeName, err.Error())
++ if config.DefaultBridge {
++ // We encountered and identified a stale default network
++ // We must delete it as libnetwork is the source of thruth
++ // The default network being created must be the only one
++ // This can happen only from docker 1.12 on ward
++ logrus.Infof("Removing stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName)
++ if err := d.DeleteNetwork(nwConfig.ID); err != nil {
++ logrus.Warnf("Failed to remove stale default network: %s (%s): %v. Will remove from store.", nwConfig.ID, nwConfig.BridgeName, err)
++ d.storeDelete(nwConfig)
++ }
++ networkList = append(networkList[:i], networkList[i+1:]...)
++ } else {
++ return types.ForbiddenErrorf(
++ "cannot create network %s (%s): "+
++ "conflicts with network %s (%s): %s",
++ nwConfig.BridgeName, config.ID, nw.id,
++ nw.config.BridgeName, err.Error())
++ }
+ }
+ }
+ if config.DefaultBindingIP == nil ||
@@ -9594,7 +10050,8 @@
+
+ // Program any required port mapping and store them in the endpoint
+ endpoint.portMapping, err = n.allocatePorts(epConfig,
-+ endpoint, c.DefaultBindingIntf, c.DefaultBindingIP)
++ endpoint, c.DefaultBindingIntf, c.DefaultBindingIP,
++ c.BridgeName+"_gw0")
+ if err != nil {
+ return err
+ }
@@ -10614,10 +11071,10 @@
+func (address InvalidLinkIPAddrError) BadRequest() {}
diff --git a/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go
new file mode 100644
-index 0000000..3fae520
+index 0000000..f2b1fd5
--- /dev/null
+++ b/vendor/src/github.com/docker/libnetwork/drivers/solaris/bridge/port_mapping.go
-@@ -0,0 +1,199 @@
+@@ -0,0 +1,218 @@
+package bridge
+
+import (
@@ -10640,7 +11097,8 @@
+ maxAllocatePortAttempts = 10
+)
+
-+func addPFRules(epid, bindIntf string, bs []types.PortBinding) {
++func addPFRules(epid, bindIntf string, bs []types.PortBinding,
++ gwIntf string) {
+ id := epid[:12]
+ fname := "/var/lib/docker/network/files/pf." + id
+
@@ -10660,6 +11118,24 @@
+ if err != nil {
+ logrus.Warnf("cannot write to %s: %v", fname, err)
+ }
++ r = fmt.Sprintf(
++ "pass out inet proto %s from (%s) to (%s) port %d " +
++ "rdr-to %s port %d route-to %s" + "@" + "%s\n",
++ b.Proto.String(), bindIntf, bindIntf, b.HostPort,
++ b.IP.String(), b.Port, b.IP.String(), gwIntf)
++ _, err = f.WriteString(r)
++ if err != nil {
++ logrus.Warnf("cannot write to %s: %v", fname, err)
++ }
++ r = fmt.Sprintf(
++ "pass out on %s inet proto %s from (%s) to %s " +
++ "port %d reply-to %s\n", gwIntf,
++ b.Proto.String(), bindIntf, b.IP.String(), b.Port,
++ bindIntf)
++ _, err = f.WriteString(r)
++ if err != nil {
++ logrus.Warnf("cannot write to %s: %v", fname, err)
++ }
+ }
+ f.Close()
+
@@ -10679,7 +11155,7 @@
+ }
+}
+
-+func (n *bridgeNetwork) allocatePorts(epc *endpointConfiguration, ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP) ([]types.PortBinding, error) {
++func (n *bridgeNetwork) allocatePorts(epc *endpointConfiguration, ep *bridgeEndpoint, bindIntf string, reqDefBindIP net.IP, gwIntf string) ([]types.PortBinding, error) {
+ if epc == nil || epc.PortBindings == nil || len(epc.PortBindings) == 0 {
+ return nil, nil
+ }
@@ -10694,7 +11170,7 @@
+ if err != nil {
+ return nil, err
+ }
-+ addPFRules(ep.id, bindIntf, bs)
++ addPFRules(ep.id, bindIntf, bs, gwIntf)
+ return bs, err
+}
+
@@ -12235,6 +12711,28 @@
+ }
+ return nil
+}
+diff --git a/vendor/src/github.com/docker/libnetwork/sandbox.go b/vendor/src/github.com/docker/libnetwork/sandbox.go
+index 71c8ebb..4853ee4 100644
+--- a/vendor/src/github.com/docker/libnetwork/sandbox.go
++++ b/vendor/src/github.com/docker/libnetwork/sandbox.go
+@@ -148,12 +148,15 @@ func (sb *sandbox) Labels() map[string]interface{} {
+ func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) {
+ m := make(map[string]*types.InterfaceStatistics)
+
+- if sb.osSbox == nil {
++ sb.Lock()
++ osb := sb.osSbox
++ sb.Unlock()
++ if osb == nil {
+ return m, nil
+ }
+
+ var err error
+- for _, i := range sb.osSbox.Info().Interfaces() {
++ for _, i := range osb.Info().Interfaces() {
+ if m[i.DstName()], err = i.Statistics(); err != nil {
+ return m, err
+ }
diff --git a/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go b/vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go
new file mode 100644
index 0000000..7569e46
@@ -13259,18 +13757,14 @@
+}
diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go b/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go
new file mode 100644
-index 0000000..27639a1
+index 0000000..503c339
--- /dev/null
+++ b/vendor/src/github.com/opencontainers/runc/libcontainer/container_solaris.go
-@@ -0,0 +1,103 @@
+@@ -0,0 +1,22 @@
+// +build solaris
+
+package libcontainer
+
-+import (
-+ "github.com/opencontainers/runc/libcontainer/zones"
-+)
-+
+// State represents a running container's state
+type State struct {
+ BaseState
@@ -13289,83 +13783,6 @@
+ // Methods below here are platform specific
+
+}
-+
-+// XXX: Should collect the networking stats too.
-+// For the stubs, need config with slice of NICs.
-+func GetStats(id string) (*Stats, error) {
-+ stats := &Stats{}
-+ zstats := zones.Stats{}
-+ cpuUsage := zones.CpuUsage {
-+ TotalUsage: 5,
-+ /* XXX: currently only TotalUsage is consumed.
-+ PercpuUsage: []uint64 { 1, 2, 3 },
-+ UsageInKernelmode: 3,
-+ UsageInUsermode: 2,
-+ */
-+ }
-+ cpuStats := zones.CpuStats {
-+ CpuUsage: cpuUsage,
-+ /* XXX: currently only CpuUsage.TotalUsage is consumed.
-+ ThrottlingData: ThrottlingData {
-+ Periods: 100,
-+ ThrottledPeriods: 5,
-+ ThrottledTime: 1,
-+ },
-+ */
-+ }
-+ memoryStats := zones.MemoryStats {
-+ Cache: 65536,
-+ Usage: zones.MemoryData {
-+ Usage: 32000000,
-+ MaxUsage: 64000000,
-+ Failcnt: 10,
-+ },
-+ /* XXX: currently only MemboriStats.Usage is consumed
-+ SwapUsage: MemoryData {
-+ Usage: 8192000,
-+ MaxUsage: 8192000,
-+ Failcnt: 128,
-+ },
-+ KernelUsage: MemoryData {
-+ Usage: 4096000,
-+ MaxUsage: 2048000,
-+ Failcnt: 0,
-+ },
-+ */
-+ }
-+ blkioStats := zones.BlkioStats {
-+ IoServiceBytesRecursive: []zones.BlkioStatEntry {
-+ {
-+ Major: 14,
-+ Minor: 1,
-+ Op: "read", //op name from api/client/stats.go
-+ Value: 9000000,
-+ },
-+ {
-+ Major: 13,
-+ Minor: 0,
-+ Op: "write", //op name from api/client/stats.go
-+ Value: 500000,
-+ },
-+ },
-+ /* XXX: currently only IoServiceBytesRecursive is consumed
-+ IoServicedRecursive: []BlkioStatEntry {
-+ {
-+ Major: 14000000,
-+ Minor: 10000000,
-+ Op: "",
-+ Value: 9000000,
-+ },
-+ },
-+ */
-+ }
-+ zstats.CpuStats = cpuStats
-+ zstats.MemoryStats = memoryStats
-+ zstats.BlkioStats = blkioStats
-+
-+ stats.Stats = &zstats
-+ return stats, nil
-+}
diff --git a/vendor/src/github.com/opencontainers/runc/libcontainer/stats_solaris.go b/vendor/src/github.com/opencontainers/runc/libcontainer/stats_solaris.go
new file mode 100644
index 0000000..7353cd8