From 8d1a500303a9dd892301e343c94821207c715fbb Mon Sep 17 00:00:00 2001 From: Richard Burnison Date: Fri, 10 Apr 2015 13:09:08 -0400 Subject: [PATCH 001/321] Only use fallback to short IDs when obvious. As reported in #11294, the Docker daemon will execute contains it shouldn't run in the event that a requested tag is present in an image's ID. This leads to the wrong image being started up silently. This change reduces the risk of such a collision by using the short ID iff the actual revOrTag looks like a short ID (not that it necessarily is). Signed-off-by: Richard Burnison --- graph/tags.go | 9 ++++++--- pkg/stringid/stringid.go | 14 +++++++++++--- pkg/stringid/stringid_test.go | 23 ++++++++++++++++++++++- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/graph/tags.go b/graph/tags.go index 6346ea8b50dc6..74d86141d448d 100644 --- a/graph/tags.go +++ b/graph/tags.go @@ -335,9 +335,12 @@ func (store *TagStore) GetImage(repoName, refOrID string) (*image.Image, error) } // If no matching tag is found, search through images for a matching image id - for _, revision := range repo { - if strings.HasPrefix(revision, refOrID) { - return store.graph.Get(revision) + // iff it looks like a short ID or would look like a short ID + if stringid.IsShortID(stringid.TruncateID(refOrID)) { + for _, revision := range repo { + if strings.HasPrefix(revision, refOrID) { + return store.graph.Get(revision) + } } } diff --git a/pkg/stringid/stringid.go b/pkg/stringid/stringid.go index bf39df9b731dd..3e6ff2a921e93 100644 --- a/pkg/stringid/stringid.go +++ b/pkg/stringid/stringid.go @@ -4,19 +4,27 @@ import ( "crypto/rand" "encoding/hex" "io" + "regexp" "strconv" ) +const shortLen = 12 + +// Determine if an arbitrary string *looks like* a short ID. +func IsShortID(id string) bool { + return regexp.MustCompile("^[a-z0-9]{12}$").MatchString(id) +} + // TruncateID returns a shorthand version of a string identifier for convenience. // A collision with other shorthands is very unlikely, but possible. // In case of a collision a lookup with TruncIndex.Get() will fail, and the caller // will need to use a langer prefix, or the full-length Id. func TruncateID(id string) string { - shortLen := 12 + trimTo := shortLen if len(id) < shortLen { - shortLen = len(id) + trimTo = len(id) } - return id[:shortLen] + return id[:trimTo] } // GenerateRandomID returns an unique id diff --git a/pkg/stringid/stringid_test.go b/pkg/stringid/stringid_test.go index 21f8f8a2fbd8c..bcb1365495524 100644 --- a/pkg/stringid/stringid_test.go +++ b/pkg/stringid/stringid_test.go @@ -1,6 +1,9 @@ package stringid -import "testing" +import ( + "strings" + "testing" +) func TestGenerateRandomID(t *testing.T) { id := GenerateRandomID() @@ -33,3 +36,21 @@ func TestShortenIdInvalid(t *testing.T) { t.Fatalf("Id returned is incorrect: truncate on %s returned %s", id, truncID) } } + +func TestIsShortIDNonHex(t *testing.T) { + id := "some non-hex value" + if IsShortID(id) { + t.Fatalf("%s is not a short ID", id) + } +} + +func TestIsShortIDNotCorrectSize(t *testing.T) { + id := strings.Repeat("a", shortLen+1) + if IsShortID(id) { + t.Fatalf("%s is not a short ID", id) + } + id = strings.Repeat("a", shortLen-1) + if IsShortID(id) { + t.Fatalf("%s is not a short ID", id) + } +} From 73bf9b5c195170b3d71f86b285ac12e50d26ef51 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Thu, 16 Apr 2015 17:36:45 +0800 Subject: [PATCH 002/321] add err check before getting term Signed-off-by: Ma Shimiao --- daemon/execdriver/lxc/driver.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/daemon/execdriver/lxc/driver.go b/daemon/execdriver/lxc/driver.go index 1637bc2c69b6f..15f57bfe0faff 100644 --- a/daemon/execdriver/lxc/driver.go +++ b/daemon/execdriver/lxc/driver.go @@ -85,16 +85,21 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba dataPath = d.containerDir(c.ID) ) + container, err := d.createContainer(c) + if err != nil { + return execdriver.ExitStatus{ExitCode: -1}, err + } + if c.ProcessConfig.Tty { term, err = NewTtyConsole(&c.ProcessConfig, pipes) } else { term, err = execdriver.NewStdConsole(&c.ProcessConfig, pipes) } - c.ProcessConfig.Terminal = term - container, err := d.createContainer(c) if err != nil { return execdriver.ExitStatus{ExitCode: -1}, err } + c.ProcessConfig.Terminal = term + d.Lock() d.activeContainers[c.ID] = &activeContainer{ container: container, From f731b01483ed7010824c5951cc4a27db907c2d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 21 Apr 2015 11:33:52 +0200 Subject: [PATCH 003/321] Dockerfile: download go libraries before copy vendor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When ever something vendor/ changes the go dependencies have to downloaded again, which requires internet access and there for is potential slow. COPY and go install is much faster, while the git urls does not change not this often. Signed-off-by: Jörg Thalheim --- Dockerfile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index b1c7c4a6f0fe8..3cfd59419190b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -160,20 +160,21 @@ RUN ./contrib/download-frozen-image.sh /docker-frozen-images \ hello-world:frozen@e45a5af57b00862e5ef5782a9925979a02ba2b12dff832fd0991335f4a11e5c5 # see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is) -# Install man page generator -COPY vendor /go/src/github.com/docker/docker/vendor -# (copy vendor/ because go-md2man needs golang.org/x/net) +# Download man page generator RUN set -x \ && git clone -b v1.0.1 https://github.com/cpuguy83/go-md2man.git /go/src/github.com/cpuguy83/go-md2man \ - && git clone -b v1.2 https://github.com/russross/blackfriday.git /go/src/github.com/russross/blackfriday \ - && go install -v github.com/cpuguy83/go-md2man + && git clone -b v1.2 https://github.com/russross/blackfriday.git /go/src/github.com/russross/blackfriday -# install toml validator +# Download toml validator ENV TOMLV_COMMIT 9baf8a8a9f2ed20a8e54160840c492f937eeaf9a RUN set -x \ && git clone https://github.com/BurntSushi/toml.git /go/src/github.com/BurntSushi/toml \ - && (cd /go/src/github.com/BurntSushi/toml && git checkout -q $TOMLV_COMMIT) \ - && go install -v github.com/BurntSushi/toml/cmd/tomlv + && (cd /go/src/github.com/BurntSushi/toml && git checkout -q $TOMLV_COMMIT) + +# copy vendor/ because go-md2man needs golang.org/x/net +COPY vendor /go/src/github.com/docker/docker/vendor +RUN go install -v github.com/cpuguy83/go-md2man \ + github.com/BurntSushi/toml/cmd/tomlv # Wrap all commands in the "docker-in-docker" script to allow nested containers ENTRYPOINT ["hack/dind"] From b3e29926cef104c9ef99ff05ed1490cf821bb7b0 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 004/321] make.sh: Define a new build tag libdm_no_deferred_remove libdm started offering deferred remove functionality from version 1.02.89. As docker still builds against older libdm, define a tag libdm_no_deferred_remove to determine whether we are compiling against new libdm or older one and enable/disable deferred remove functionality accordingly. Signed-off-by: Vincent Batts Signed-off-by: Vivek Goyal --- hack/make.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hack/make.sh b/hack/make.sh index 3bcb265b3c3b9..aa98ef128d1c8 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -107,6 +107,15 @@ if \ DOCKER_BUILDTAGS+=' btrfs_noversion' fi +# test whether "libdevmapper.h" is new enough to support deferred remove +# functionality. +if \ + command -v gcc &> /dev/null \ + && ! ( echo -e '#include \nint main() { dm_task_deferred_remove(NULL); }'| gcc -ldevmapper -xc - &> /dev/null ) \ +; then + DOCKER_BUILDTAGS+=' libdm_no_deferred_remove' +fi + # Use these flags when compiling the tests and final binary IAMSTATIC='true' From 6964ab94befd8723585556e560219e0eef48a488 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 005/321] devicemapper: Add helper functions to allow deferred device removal A lot of time device mapper devices leak across mount namespace which docker does not know about and when docker tries to deactivate/delete device, operation fails as device is open in some mount namespace. Create a mechanism where one can defer the device deactivation/deletion so that docker operation does not fail and device automatically goes away when last reference to it is dropped. Signed-off-by: Vivek Goyal --- pkg/devicemapper/devmapper.go | 20 +++++++++++++++++++ pkg/devicemapper/devmapper_wrapper.go | 1 + .../devmapper_wrapper_deferred_remove.go | 15 ++++++++++++++ .../devmapper_wrapper_no_deferred_remove.go | 10 ++++++++++ 4 files changed, 46 insertions(+) create mode 100644 pkg/devicemapper/devmapper_wrapper_deferred_remove.go create mode 100644 pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go diff --git a/pkg/devicemapper/devmapper.go b/pkg/devicemapper/devmapper.go index bb89f7fac213b..42876d60c3748 100644 --- a/pkg/devicemapper/devmapper.go +++ b/pkg/devicemapper/devmapper.go @@ -55,6 +55,7 @@ var ( ErrTaskGetDeps = errors.New("dm_task_get_deps failed") ErrTaskGetInfo = errors.New("dm_task_get_info failed") ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed") + ErrTaskDeferredRemove = errors.New("dm_task_deferred_remove failed") ErrTaskSetCookie = errors.New("dm_task_set_cookie failed") ErrNilCookie = errors.New("cookie ptr can't be nil") ErrAttachLoopbackDevice = errors.New("loopback mounting failed") @@ -371,6 +372,25 @@ func RemoveDevice(name string) error { return nil } +func RemoveDeviceDeferred(name string) error { + logrus.Debugf("[devmapper] RemoveDeviceDeferred START(%s)", name) + defer logrus.Debugf("[devmapper] RemoveDeviceDeferred END(%s)", name) + task, err := TaskCreateNamed(DeviceRemove, name) + if task == nil { + return err + } + + if err := DmTaskDeferredRemove(task.unmanaged); err != 1 { + return ErrTaskDeferredRemove + } + + if err = task.Run(); err != nil { + return fmt.Errorf("Error running RemoveDeviceDeferred %s", err) + } + + return nil +} + func GetBlockDeviceSize(file *os.File) (uint64, error) { size, err := ioctlBlkGetSize64(file.Fd()) if err != nil { diff --git a/pkg/devicemapper/devmapper_wrapper.go b/pkg/devicemapper/devmapper_wrapper.go index e436cca32dac1..fc841d952fc99 100644 --- a/pkg/devicemapper/devmapper_wrapper.go +++ b/pkg/devicemapper/devmapper_wrapper.go @@ -112,6 +112,7 @@ var ( DmUdevGetSyncSupport = dmUdevGetSyncSupportFct DmCookieSupported = dmCookieSupportedFct LogWithErrnoInit = logWithErrnoInitFct + DmTaskDeferredRemove = dmTaskDeferredRemoveFct ) func free(p *C.char) { diff --git a/pkg/devicemapper/devmapper_wrapper_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_deferred_remove.go new file mode 100644 index 0000000000000..3d52f3fffa054 --- /dev/null +++ b/pkg/devicemapper/devmapper_wrapper_deferred_remove.go @@ -0,0 +1,15 @@ +// +build linux,!libdm_no_deferred_remove + +package devicemapper + +/* +#cgo LDFLAGS: -L. -ldevmapper +#include +*/ +import "C" + +const LibraryDeferredRemovalSupport = true + +func dmTaskDeferredRemoveFct(task *CDmTask) int { + return int(C.dm_task_deferred_remove((*C.struct_dm_task)(task))) +} diff --git a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go new file mode 100644 index 0000000000000..6366065fd78be --- /dev/null +++ b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go @@ -0,0 +1,10 @@ +// +build linux,libdm_no_deferred_remove + +package devicemapper + +const LibraryDeferredRemovalSupport = false + +func dmTaskDeferredRemoveFct(task *CDmTask) int { + // Error. Nobody should be calling it. + return -1 +} From 15c158b20725fd62e2ee0a72ffaf1617852cd0d9 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 006/321] devmapper: Provide a new parameter dm.deferred_device_removal Provide a new command line knob dm.deferred_device_removal which will enable deferred device deactivation if driver and library support it. This patch also checks for library support and driver version. Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/README.md | 20 +++++++ daemon/graphdriver/devmapper/deviceset.go | 65 ++++++++++++++++++++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md index a090b731faf8c..bd5b67c49c603 100644 --- a/daemon/graphdriver/devmapper/README.md +++ b/daemon/graphdriver/devmapper/README.md @@ -252,3 +252,23 @@ Here is the list of supported options: > Otherwise, set this flag for migrating existing Docker daemons to a > daemon with a supported environment. + * `dm.use_deferred_removal` + + Enables use of deferred device removal if libdm and kernel driver + support the mechanism. + + Deferred device removal means that if device is busy when devices is + being removed/deactivated, then a deferred removal is scheduled on + device. And devices automatically goes away when last user of device + exits. + + For example, when contianer exits, its associated thin device is + removed. If that devices has leaked into some other mount namespace + can can't be removed now, container exit will still be successful + and this option will just schedule device for deferred removal and + will not wait in a loop trying to remove a busy device. + + Example use: + + ``docker -d --storage-opt dm.use_deferred_device_removal=true`` + diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index b5d67fa119d0c..0856758cb72f7 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -37,7 +37,9 @@ var ( // We retry device removal so many a times that even error messages // will fill up console during normal operation. So only log Fatal // messages by default. - DMLogLevel int = devicemapper.LogLevelFatal + DMLogLevel int = devicemapper.LogLevelFatal + DriverDeferredRemovalSupport bool = false + EnableDeferredRemoval bool = false ) const deviceSetMetaFile string = "deviceset-metadata" @@ -103,6 +105,7 @@ type DeviceSet struct { thinPoolDevice string Transaction `json:"-"` overrideUdevSyncCheck bool + deferredRemove bool // use deferred removal } type DiskUsage struct { @@ -960,16 +963,67 @@ func (devices *DeviceSet) closeTransaction() error { return nil } +func determineDriverCapabilities(version string) error { + /* + * Driver version 4.27.0 and greater support deferred activation + * feature. + */ + + logrus.Debugf("devicemapper: driver version is %s", version) + + versionSplit := strings.Split(version, ".") + major, err := strconv.Atoi(versionSplit[0]) + if err != nil { + return graphdriver.ErrNotSupported + } + + if major > 4 { + DriverDeferredRemovalSupport = true + return nil + } + + if major < 4 { + return nil + } + + minor, err := strconv.Atoi(versionSplit[1]) + if err != nil { + return graphdriver.ErrNotSupported + } + + /* + * If major is 4 and minor is 27, then there is no need to + * check for patch level as it can not be less than 0. + */ + if minor >= 27 { + DriverDeferredRemovalSupport = true + return nil + } + + return nil +} + func (devices *DeviceSet) initDevmapper(doInit bool) error { // give ourselves to libdm as a log handler devicemapper.LogInit(devices) - _, err := devicemapper.GetDriverVersion() + version, err := devicemapper.GetDriverVersion() if err != nil { // Can't even get driver version, assume not supported return graphdriver.ErrNotSupported } + if err := determineDriverCapabilities(version); err != nil { + return graphdriver.ErrNotSupported + } + + // If user asked for deferred removal and both library and driver + // supports deferred removal use it. + if EnableDeferredRemoval && DriverDeferredRemovalSupport && devicemapper.LibraryDeferredRemovalSupport == true { + logrus.Debugf("devmapper: Deferred removal support enabled.") + devices.deferredRemove = true + } + // 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") @@ -1671,6 +1725,13 @@ func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error if err != nil { return nil, err } + + case "dm.use_deferred_removal": + EnableDeferredRemoval, err = strconv.ParseBool(val) + if err != nil { + return nil, err + } + default: return nil, fmt.Errorf("Unknown option %s\n", key) } From e37c7203bb1d840e9383ac08bf87afda3e722344 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 007/321] devmapper: Use deferred removal Make use of deferred removal of devices. Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/deviceset.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 0856758cb72f7..99bffacd53cf9 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -1287,12 +1287,20 @@ func (devices *DeviceSet) deactivateDevice(info *DevInfo) error { if err != nil { return err } - if devinfo.Exists != 0 { + + if devinfo.Exists == 0 { + return nil + } + + if devices.deferredRemove { + if err := devicemapper.RemoveDeviceDeferred(info.Name()); err != nil { + return err + } + } else { if err := devices.removeDevice(info.Name()); err != nil { return err } } - return nil } From 66a53819aea2ab1ab0d50be1f8d32fcb2427cd78 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 008/321] devmapper: Export deferred removal status in status This will help with debugging as one could just do "docker info" and figure out of deferred removal is enabled or not. Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/deviceset.go | 20 +++++++++++--------- daemon/graphdriver/devmapper/driver.go | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 99bffacd53cf9..8dccb2ed876d9 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -115,15 +115,16 @@ type DiskUsage struct { } type Status struct { - PoolName string - DataFile string // actual block device for data - DataLoopback string // loopback file, if used - MetadataFile string // actual block device for metadata - MetadataLoopback string // loopback file, if used - Data DiskUsage - Metadata DiskUsage - SectorSize uint64 - UdevSyncSupported bool + PoolName string + DataFile string // actual block device for data + DataLoopback string // loopback file, if used + MetadataFile string // actual block device for metadata + MetadataLoopback string // loopback file, if used + Data DiskUsage + Metadata DiskUsage + SectorSize uint64 + UdevSyncSupported bool + DeferredRemoveEnabled bool } type DevStatus struct { @@ -1623,6 +1624,7 @@ func (devices *DeviceSet) Status() *Status { status.MetadataFile = devices.MetadataDevicePath() status.MetadataLoopback = devices.metadataLoopFile status.UdevSyncSupported = devicemapper.UdevSyncSupported() + status.DeferredRemoveEnabled = devices.deferredRemove totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus() if err == nil { diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index fad0a0c55d36c..bdf7f874f9296 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -77,6 +77,7 @@ func (d *Driver) Status() [][2]string { {"Metadata Space Total", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Total)))}, {"Metadata Space Available", fmt.Sprintf("%s", units.HumanSize(float64(s.Metadata.Available)))}, {"Udev Sync Supported", fmt.Sprintf("%v", s.UdevSyncSupported)}, + {"Deferred Removal Enabled", fmt.Sprintf("%v", s.DeferredRemoveEnabled)}, } if len(s.DataLoopback) > 0 { status = append(status, [2]string{"Data loop file", s.DataLoopback}) From 20b38f427aa05186bd09c8c4201dcc95ed56aa46 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 009/321] devicemapper: Create helpers to cancel deferred deactivation If a device has been scheduled for deferred deactivation and container is started again and we need to activate device again, we need to cancel the deferred deactivation which is already scheduled on the device. Create a method for the same. Signed-off-by: Vivek Goyal --- pkg/devicemapper/devmapper.go | 32 +++++++++++++++++++++++++++++++ pkg/devicemapper/devmapper_log.go | 4 ++++ 2 files changed, 36 insertions(+) diff --git a/pkg/devicemapper/devmapper.go b/pkg/devicemapper/devmapper.go index 42876d60c3748..04ad912847b46 100644 --- a/pkg/devicemapper/devmapper.go +++ b/pkg/devicemapper/devmapper.go @@ -70,9 +70,11 @@ var ( ErrLoopbackSetCapacity = errors.New("Unable set loopback capacity") ErrBusy = errors.New("Device is Busy") ErrDeviceIdExists = errors.New("Device Id Exists") + ErrEnxio = errors.New("No such device or address") dmSawBusy bool dmSawExist bool + dmSawEnxio bool // No Such Device or Address ) type ( @@ -391,6 +393,36 @@ func RemoveDeviceDeferred(name string) error { return nil } +// Useful helper for cleanup +func CancelDeferredRemove(deviceName string) error { + task, err := TaskCreateNamed(DeviceTargetMsg, deviceName) + if task == nil { + return err + } + + if err := task.SetSector(0); err != nil { + return fmt.Errorf("Can't set sector %s", err) + } + + if err := task.SetMessage(fmt.Sprintf("@cancel_deferred_remove")); err != nil { + return fmt.Errorf("Can't set message %s", err) + } + + dmSawBusy = false + dmSawEnxio = false + if err := task.Run(); err != nil { + // A device might be being deleted already + if dmSawBusy { + return ErrBusy + } else if dmSawEnxio { + return ErrEnxio + } + return fmt.Errorf("Error running CancelDeferredRemove %s", err) + + } + return nil +} + func GetBlockDeviceSize(file *os.File) (uint64, error) { size, err := ioctlBlkGetSize64(file.Fd()) if err != nil { diff --git a/pkg/devicemapper/devmapper_log.go b/pkg/devicemapper/devmapper_log.go index d6550bd626fe4..f66a20884b6c2 100644 --- a/pkg/devicemapper/devmapper_log.go +++ b/pkg/devicemapper/devmapper_log.go @@ -22,6 +22,10 @@ func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_cla if strings.Contains(msg, "File exists") { dmSawExist = true } + + if strings.Contains(msg, "No such device or address") { + dmSawEnxio = true + } } if dmLogger != nil { From 4986ce7cfbe74610d4fa2c4e79ceefe49c1aa155 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 010/321] devicemapper: Create a method to get device info with deferred remove field Deferred reove functionality was added to library later. So in old version of library it did not report deferred_remove field. Create a new function which also gets deferred_remove field and it will be called only on newer version of library. Signed-off-by: Vivek Goyal --- pkg/devicemapper/devmapper.go | 40 ++++++++++++---- pkg/devicemapper/devmapper_wrapper.go | 47 ++++++++++--------- .../devmapper_wrapper_deferred_remove.go | 18 +++++++ .../devmapper_wrapper_no_deferred_remove.go | 4 ++ 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/pkg/devicemapper/devmapper.go b/pkg/devicemapper/devmapper.go index 04ad912847b46..e7f17b88c4531 100644 --- a/pkg/devicemapper/devmapper.go +++ b/pkg/devicemapper/devmapper.go @@ -87,16 +87,17 @@ type ( Device []uint64 } Info struct { - Exists int - Suspended int - LiveTable int - InactiveTable int - OpenCount int32 - EventNr uint32 - Major uint32 - Minor uint32 - ReadOnly int - TargetCount int32 + Exists int + Suspended int + LiveTable int + InactiveTable int + OpenCount int32 + EventNr uint32 + Major uint32 + Minor uint32 + ReadOnly int + TargetCount int32 + DeferredRemove int } TaskType int AddNodeType int @@ -222,6 +223,14 @@ func (t *Task) GetInfo() (*Info, error) { return info, nil } +func (t *Task) GetInfoWithDeferred() (*Info, error) { + info := &Info{} + if res := DmTaskGetInfoWithDeferred(t.unmanaged, info); res != 1 { + return nil, ErrTaskGetInfo + } + return info, nil +} + func (t *Task) GetDriverVersion() (string, error) { res := DmTaskGetDriverVersion(t.unmanaged) if res == "" { @@ -531,6 +540,17 @@ func GetInfo(name string) (*Info, error) { return task.GetInfo() } +func GetInfoWithDeferred(name string) (*Info, error) { + task, err := TaskCreateNamed(DeviceInfo, name) + if task == nil { + return nil, err + } + if err := task.Run(); err != nil { + return nil, err + } + return task.GetInfoWithDeferred() +} + func GetDriverVersion() (string, error) { task := TaskCreate(DeviceVersion) if task == nil { diff --git a/pkg/devicemapper/devmapper_wrapper.go b/pkg/devicemapper/devmapper_wrapper.go index fc841d952fc99..87c200376f35c 100644 --- a/pkg/devicemapper/devmapper_wrapper.go +++ b/pkg/devicemapper/devmapper_wrapper.go @@ -90,29 +90,30 @@ const ( ) var ( - DmGetLibraryVersion = dmGetLibraryVersionFct - DmGetNextTarget = dmGetNextTargetFct - DmLogInitVerbose = dmLogInitVerboseFct - DmSetDevDir = dmSetDevDirFct - DmTaskAddTarget = dmTaskAddTargetFct - DmTaskCreate = dmTaskCreateFct - DmTaskDestroy = dmTaskDestroyFct - DmTaskGetDeps = dmTaskGetDepsFct - DmTaskGetInfo = dmTaskGetInfoFct - DmTaskGetDriverVersion = dmTaskGetDriverVersionFct - DmTaskRun = dmTaskRunFct - DmTaskSetAddNode = dmTaskSetAddNodeFct - DmTaskSetCookie = dmTaskSetCookieFct - DmTaskSetMessage = dmTaskSetMessageFct - DmTaskSetName = dmTaskSetNameFct - DmTaskSetRo = dmTaskSetRoFct - DmTaskSetSector = dmTaskSetSectorFct - DmUdevWait = dmUdevWaitFct - DmUdevSetSyncSupport = dmUdevSetSyncSupportFct - DmUdevGetSyncSupport = dmUdevGetSyncSupportFct - DmCookieSupported = dmCookieSupportedFct - LogWithErrnoInit = logWithErrnoInitFct - DmTaskDeferredRemove = dmTaskDeferredRemoveFct + DmGetLibraryVersion = dmGetLibraryVersionFct + DmGetNextTarget = dmGetNextTargetFct + DmLogInitVerbose = dmLogInitVerboseFct + DmSetDevDir = dmSetDevDirFct + DmTaskAddTarget = dmTaskAddTargetFct + DmTaskCreate = dmTaskCreateFct + DmTaskDestroy = dmTaskDestroyFct + DmTaskGetDeps = dmTaskGetDepsFct + DmTaskGetInfo = dmTaskGetInfoFct + DmTaskGetDriverVersion = dmTaskGetDriverVersionFct + DmTaskRun = dmTaskRunFct + DmTaskSetAddNode = dmTaskSetAddNodeFct + DmTaskSetCookie = dmTaskSetCookieFct + DmTaskSetMessage = dmTaskSetMessageFct + DmTaskSetName = dmTaskSetNameFct + DmTaskSetRo = dmTaskSetRoFct + DmTaskSetSector = dmTaskSetSectorFct + DmUdevWait = dmUdevWaitFct + DmUdevSetSyncSupport = dmUdevSetSyncSupportFct + DmUdevGetSyncSupport = dmUdevGetSyncSupportFct + DmCookieSupported = dmCookieSupportedFct + LogWithErrnoInit = logWithErrnoInitFct + DmTaskDeferredRemove = dmTaskDeferredRemoveFct + DmTaskGetInfoWithDeferred = dmTaskGetInfoWithDeferredFct ) func free(p *C.char) { diff --git a/pkg/devicemapper/devmapper_wrapper_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_deferred_remove.go index 3d52f3fffa054..ced482c9658a9 100644 --- a/pkg/devicemapper/devmapper_wrapper_deferred_remove.go +++ b/pkg/devicemapper/devmapper_wrapper_deferred_remove.go @@ -13,3 +13,21 @@ const LibraryDeferredRemovalSupport = true func dmTaskDeferredRemoveFct(task *CDmTask) int { return int(C.dm_task_deferred_remove((*C.struct_dm_task)(task))) } + +func dmTaskGetInfoWithDeferredFct(task *CDmTask, info *Info) int { + Cinfo := C.struct_dm_info{} + defer func() { + info.Exists = int(Cinfo.exists) + info.Suspended = int(Cinfo.suspended) + info.LiveTable = int(Cinfo.live_table) + info.InactiveTable = int(Cinfo.inactive_table) + info.OpenCount = int32(Cinfo.open_count) + info.EventNr = uint32(Cinfo.event_nr) + info.Major = uint32(Cinfo.major) + info.Minor = uint32(Cinfo.minor) + info.ReadOnly = int(Cinfo.read_only) + info.TargetCount = int32(Cinfo.target_count) + info.DeferredRemove = int(Cinfo.deferred_remove) + }() + return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) +} diff --git a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go index 6366065fd78be..16631bf19ceff 100644 --- a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go +++ b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go @@ -8,3 +8,7 @@ func dmTaskDeferredRemoveFct(task *CDmTask) int { // Error. Nobody should be calling it. return -1 } + +func dmTaskGetInfoWithDeferredFct(task *CDmTask, info *Info) int { + return -1 +} From ddc8acebecfdc7dbc0357f5c009fb3ee0a2ae906 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 21 Apr 2015 18:14:59 -0400 Subject: [PATCH 011/321] devmapper: Cancel deferred deactivation if device is reactivated If device is being reactivated before it could go away and deferred deactivation is scheduled on it, cancel it. Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/deviceset.go | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 8dccb2ed876d9..3d4e64eb9c58c 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -438,6 +438,12 @@ func (devices *DeviceSet) registerDevice(id int, hash string, size uint64, trans func (devices *DeviceSet) activateDeviceIfNeeded(info *DevInfo) error { logrus.Debugf("activateDeviceIfNeeded(%v)", info.Hash) + // Make sure deferred removal on device is canceled, if one was + // scheduled. + if err := devices.cancelDeferredRemoval(info); err != nil { + return fmt.Errorf("Deivce Deferred Removal Cancellation Failed: %s", err) + } + if devinfo, _ := devicemapper.GetInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 { return nil } @@ -1331,6 +1337,45 @@ func (devices *DeviceSet) removeDevice(devname string) error { return err } +func (devices *DeviceSet) cancelDeferredRemoval(info *DevInfo) error { + if !devices.deferredRemove { + return nil + } + + logrus.Debugf("[devmapper] cancelDeferredRemoval START(%s)", info.Name()) + defer logrus.Debugf("[devmapper] cancelDeferredRemoval END(%s)", info.Name) + + devinfo, err := devicemapper.GetInfoWithDeferred(info.Name()) + + if devinfo != nil && devinfo.DeferredRemove == 0 { + return nil + } + + // Cancel deferred remove + for i := 0; i < 100; i++ { + err = devicemapper.CancelDeferredRemove(info.Name()) + if err == nil { + break + } + + if err == devicemapper.ErrEnxio { + // Device is probably already gone. Return success. + return nil + } + + if err != devicemapper.ErrBusy { + return err + } + + // If we see EBUSY it may be a transient error, + // sleep a bit a retry a few times. + devices.Unlock() + time.Sleep(100 * time.Millisecond) + devices.Lock() + } + return err +} + func (devices *DeviceSet) Shutdown() error { logrus.Debugf("[deviceset %s] Shutdown()", devices.devicePrefix) logrus.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root) From 05418df539dfed118da099aacfe4250f2f6ad5e0 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Thu, 23 Apr 2015 11:11:23 +0800 Subject: [PATCH 012/321] sysinfo: add IPv4Forwarding check Signed-off-by: Ma Shimiao --- pkg/sysinfo/sysinfo.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 76a61fa95f059..57e5563a893ee 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -4,6 +4,8 @@ import ( "io/ioutil" "os" "path" + "strconv" + "strings" "github.com/Sirupsen/logrus" "github.com/docker/libcontainer/cgroups" @@ -52,6 +54,17 @@ func New(quiet bool) *SysInfo { } } + // Checek if ipv4_forward is disabled. + if data, err := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward"); os.IsNotExist(err) { + sysInfo.IPv4ForwardingDisabled = true + } else { + if enabled, _ := strconv.Atoi(strings.TrimSpace(string(data))); enabled == 0 { + sysInfo.IPv4ForwardingDisabled = true + } else { + sysInfo.IPv4ForwardingDisabled = false + } + } + // Check if AppArmor is supported. if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) { sysInfo.AppArmor = false From daad696e091c931c8f0bf210c65d90db27b0af74 Mon Sep 17 00:00:00 2001 From: He Simei Date: Sat, 25 Apr 2015 09:10:32 +0800 Subject: [PATCH 013/321] Fix error prompt for pull & import handler postImagesCreate. Signed-off-by: He Simei --- api/server/server.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/api/server/server.go b/api/server/server.go index cdc6c181548b8..65cfd8a963abb 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -740,8 +740,9 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w } var ( - opErr error + err error useJSON = version.GreaterThan("1.0") + output = utils.NewWriteFlusher(w) ) if useJSON { @@ -763,11 +764,12 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w Parallel: version.GreaterThan("1.3"), MetaHeaders: metaHeaders, AuthConfig: authConfig, - OutStream: utils.NewWriteFlusher(w), + OutStream: output, Json: useJSON, } - opErr = s.daemon.Repositories().Pull(image, tag, imagePullConfig) + err = s.daemon.Repositories().Pull(image, tag, imagePullConfig) + } else { //import if tag == "" { repo, tag = parsers.ParseRepositoryTag(repo) @@ -777,7 +779,7 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w imageImportConfig := &graph.ImageImportConfig{ Changes: r.Form["changes"], InConfig: r.Body, - OutStream: utils.NewWriteFlusher(w), + OutStream: output, Json: useJSON, } @@ -787,15 +789,19 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w } imageImportConfig.ContainerConfig = newConfig - opErr = s.daemon.Repositories().Import(src, repo, tag, imageImportConfig) - } + err = s.daemon.Repositories().Import(src, repo, tag, imageImportConfig) - if opErr != nil { + } + if err != nil { + if !output.Flushed() { + return err + } sf := streamformatter.NewStreamFormatter(useJSON) - return fmt.Errorf(string(sf.FormatError(opErr))) + output.Write(sf.FormatError(err)) } return nil + } func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { From bb411939989dabb56eb604b9a873d1dfbf0646ef Mon Sep 17 00:00:00 2001 From: Julien Barbier Date: Thu, 26 Mar 2015 23:14:31 +0000 Subject: [PATCH 014/321] Happy birthday Docker! cgroup-parent option for docker build. Thanks to Michael, Nathan and Jessie for their support! #42 Signed-off-by: Julien Barbier --- api/client/build.go | 2 ++ api/server/server.go | 1 + builder/evaluator.go | 13 +++++++------ builder/internals.go | 13 +++++++------ builder/job.go | 2 ++ 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/api/client/build.go b/api/client/build.go index 800e04ac9b94d..107a1995f6a4e 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -58,6 +58,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { flCpuQuota := cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota") flCPUSetCpus := cmd.String([]string{"-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)") flCPUSetMems := cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") + flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) @@ -276,6 +277,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { v.Set("cpuquota", strconv.FormatInt(*flCpuQuota, 10)) v.Set("memory", strconv.FormatInt(memory, 10)) v.Set("memswap", strconv.FormatInt(memorySwap, 10)) + v.Set("cgroupparent", *flCgroupParent) v.Set("dockerfile", *dockerfileName) diff --git a/api/server/server.go b/api/server/server.go index cdc6c181548b8..08932b90e8b6b 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -1346,6 +1346,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R buildConfig.CpuQuota = int64Value(r, "cpuquota") buildConfig.CpuSetCpus = r.FormValue("cpusetcpus") buildConfig.CpuSetMems = r.FormValue("cpusetmems") + buildConfig.CgroupParent = r.FormValue("cgroupparent") // Job cancellation. Note: not all job types support this. if closeNotifier, ok := w.(http.CloseNotifier); ok { diff --git a/builder/evaluator.go b/builder/evaluator.go index 9a2b57a8f93ea..214499bc0209a 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -121,12 +121,13 @@ type Builder struct { noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system. // Set resource restrictions for build containers - cpuSetCpus string - cpuSetMems string - cpuShares int64 - cpuQuota int64 - memory int64 - memorySwap int64 + cpuSetCpus string + cpuSetMems string + cpuShares int64 + cpuQuota int64 + cgroupParent string + memory int64 + memorySwap int64 cancelled <-chan struct{} // When closed, job was cancelled. } diff --git a/builder/internals.go b/builder/internals.go index ba7d45bcb18cc..7c3b924e773df 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -546,12 +546,13 @@ func (b *Builder) create() (*daemon.Container, error) { b.Config.Image = b.image hostConfig := &runconfig.HostConfig{ - CpuShares: b.cpuShares, - CpuQuota: b.cpuQuota, - CpusetCpus: b.cpuSetCpus, - CpusetMems: b.cpuSetMems, - Memory: b.memory, - MemorySwap: b.memorySwap, + CpuShares: b.cpuShares, + CpuQuota: b.cpuQuota, + CpusetCpus: b.cpuSetCpus, + CpusetMems: b.cpuSetMems, + CgroupParent: b.cgroupParent, + Memory: b.memory, + MemorySwap: b.memorySwap, } config := *b.Config diff --git a/builder/job.go b/builder/job.go index 0ad488aae855c..a64375c96bdf7 100644 --- a/builder/job.go +++ b/builder/job.go @@ -52,6 +52,7 @@ type Config struct { CpuQuota int64 CpuSetCpus string CpuSetMems string + CgroupParent string AuthConfig *cliconfig.AuthConfig ConfigFile *cliconfig.ConfigFile @@ -166,6 +167,7 @@ func Build(d *daemon.Daemon, buildConfig *Config) error { cpuQuota: buildConfig.CpuQuota, cpuSetCpus: buildConfig.CpuSetCpus, cpuSetMems: buildConfig.CpuSetMems, + cgroupParent: buildConfig.CgroupParent, memory: buildConfig.Memory, memorySwap: buildConfig.MemorySwap, cancelled: buildConfig.WaitCancelled(), From 81897adcee8813efdff8374a1f46125b7e094847 Mon Sep 17 00:00:00 2001 From: Julien Barbier Date: Thu, 26 Mar 2015 23:45:17 +0000 Subject: [PATCH 015/321] Adding doc Signed-off-by: Julien Barbier --- docs/sources/reference/commandline/cli.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 26659c8ffa199..072d7b40cf332 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -627,8 +627,9 @@ is returned by the `docker attach` command to its caller too: -m, --memory="" Memory limit for all build containers --memory-swap="" Total memory (memory + swap), `-1` to disable swap -c, --cpu-shares CPU Shares (relative weight) - --cpuset-cpus="" CPUs in which to allow execution, e.g. `0-3`, `0,1` --cpuset-mems="" MEMs in which to allow execution, e.g. `0-3`, `0,1` + --cpuset-cpus="" CPUs in which to allow exection, e.g. `0-3`, `0,1` + --cgroup-parent="" Optional parent cgroup for the container Builds Docker images from a Dockerfile and a "context". A build's context is the files located in the specified `PATH` or `URL`. The build process can @@ -847,6 +848,9 @@ you refer to it on the command line. > children) for security reasons, and to ensure repeatable builds on remote > Docker hosts. This is also the reason why `ADD ../file` will not work. +`docker build` has a `--cgroup-parent` option that causes the containers used +in the build to be run with this option. + ## commit Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] From f40dd69c97a5a3797f07d52fe5f76e296ef629dc Mon Sep 17 00:00:00 2001 From: Marianna Date: Fri, 27 Mar 2015 19:18:24 -0700 Subject: [PATCH 016/321] Add test for cgroup parent flag for build Signed-off-by: Marianna --- integration-cli/docker_cli_build_test.go | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 6d6805aef5480..fc5f13539a0dc 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5251,3 +5251,30 @@ func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) { } } + +func TestBuildContainerWithCgroupParent(t *testing.T) { + testRequires(t, NativeExecDriver) + defer deleteImages() + + cgroupParent := "test" + data, err := ioutil.ReadFile("/proc/self/cgroup") + if err != nil { + t.Fatalf("failed to read '/proc/self/cgroup - %v", err) + } + selfCgroupPaths := parseCgroupPaths(string(data)) + _, found := selfCgroupPaths["memory"] + if !found { + t.Fatalf("unable to find self cpu cgroup path. CgroupsPath: %v", selfCgroupPaths) + } + cmd := exec.Command(dockerBinary, "build", "--cgroup-parent", cgroupParent , "-") + cmd.Stdin = strings.NewReader(` +FROM busybox +RUN cat /proc/self/cgroup +`) + + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err) + } + logDone("build - cgroup parent") +} From f039c699a40397b71f05838e9a9c67100ae474c6 Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Tue, 31 Mar 2015 18:27:53 -0700 Subject: [PATCH 017/321] Fix gofmt Mischevious comma is mischevious Signed-off-by: Nathan LeClaire --- integration-cli/docker_cli_build_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index fc5f13539a0dc..35ea20f56fbae 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5266,7 +5266,7 @@ func TestBuildContainerWithCgroupParent(t *testing.T) { if !found { t.Fatalf("unable to find self cpu cgroup path. CgroupsPath: %v", selfCgroupPaths) } - cmd := exec.Command(dockerBinary, "build", "--cgroup-parent", cgroupParent , "-") + cmd := exec.Command(dockerBinary, "build", "--cgroup-parent", cgroupParent, "-") cmd.Stdin = strings.NewReader(` FROM busybox RUN cat /proc/self/cgroup From 65aba0c9d6a72fc89f87c328e9ca21e20bf3cf7a Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Fri, 17 Apr 2015 15:35:56 -0700 Subject: [PATCH 018/321] Add dep to test Signed-off-by: Nathan LeClaire --- integration-cli/docker_cli_build_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 35ea20f56fbae..93affbc5f938b 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5254,6 +5254,7 @@ func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) { func TestBuildContainerWithCgroupParent(t *testing.T) { testRequires(t, NativeExecDriver) + testRequires(t, SameHostDaemon) defer deleteImages() cgroupParent := "test" From a8dfafc98642455ed83f9422f3174fa828dde4df Mon Sep 17 00:00:00 2001 From: Marianna Date: Fri, 17 Apr 2015 19:01:16 -0700 Subject: [PATCH 019/321] Make the docs for --cgroup-parent better Signed-off-by: Marianna --- docs/sources/reference/commandline/cli.md | 6 ++++-- docs/sources/reference/run.md | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 072d7b40cf332..3ffa5bd5c1f3c 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -848,8 +848,10 @@ you refer to it on the command line. > children) for security reasons, and to ensure repeatable builds on remote > Docker hosts. This is also the reason why `ADD ../file` will not work. -`docker build` has a `--cgroup-parent` option that causes the containers used -in the build to be run with this option. +When `docker build` is run with the `--cgroup-parent` option the containers used +in the build will be run with the [corresponding `docker run` +flag](/reference/run/#specifying-custom-cgroups). + ## commit diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 7218fab649299..0cd43eaacdaf3 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -465,6 +465,13 @@ Note: You would have to write policy defining a `svirt_apache_t` type. +## Specifying custom cgroups + +Using the `--cgroup-parent` flag, you can pass a specific cgroup to run a +container in. This allows you to create and manage cgroups on their own. You can +define custom resources for those cgroups and put containers under a common +parent group. + ## Runtime constraints on resources The operator can also adjust the performance parameters of the From 9dbe12b792a46538c01862930c85ace18dbe14f3 Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Sat, 25 Apr 2015 16:42:30 -0400 Subject: [PATCH 020/321] Migrate integration test to new method Signed-off-by: Nathan LeClaire --- integration-cli/docker_cli_build_test.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 93affbc5f938b..dd8635f95372a 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5252,20 +5252,20 @@ func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) { } -func TestBuildContainerWithCgroupParent(t *testing.T) { - testRequires(t, NativeExecDriver) - testRequires(t, SameHostDaemon) +func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *check.C) { + testRequires(c, NativeExecDriver) + testRequires(c, SameHostDaemon) defer deleteImages() cgroupParent := "test" data, err := ioutil.ReadFile("/proc/self/cgroup") if err != nil { - t.Fatalf("failed to read '/proc/self/cgroup - %v", err) + c.Fatalf("failed to read '/proc/self/cgroup - %v", err) } selfCgroupPaths := parseCgroupPaths(string(data)) _, found := selfCgroupPaths["memory"] if !found { - t.Fatalf("unable to find self cpu cgroup path. CgroupsPath: %v", selfCgroupPaths) + c.Fatalf("unable to find self cpu cgroup path. CgroupsPath: %v", selfCgroupPaths) } cmd := exec.Command(dockerBinary, "build", "--cgroup-parent", cgroupParent, "-") cmd.Stdin = strings.NewReader(` @@ -5275,7 +5275,6 @@ RUN cat /proc/self/cgroup out, _, err := runCommandWithOutput(cmd) if err != nil { - t.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err) + c.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err) } - logDone("build - cgroup parent") } From f930c86cb1ea94525ecc8a0331277afad99d4c1a Mon Sep 17 00:00:00 2001 From: Jinsoo Park Date: Thu, 26 Mar 2015 14:14:51 -0400 Subject: [PATCH 021/321] add library files for socat in mkimage-unittest.sh in https://docs.docker.com/articles/ambassador_pattern_linking/ svendowideit/ambassador images is from docker-ut built using this script and uses socat but socat complains as follows socat: error while loading shared libraries: libreadline.so.5: cannot open shared object file: No such file or directory socat: error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory socat: error while loading shared libraries: libcrypto.so.1.0.0: cannot open shared object file: No such file or directory socat: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory /usr/lib/x86_64-linux-gnu/lib{crypto,ssl}.so* lib are symlinks so removing -P option from cp adding libreadline.so and libtinfo.so Signed-off-by: Jinsoo Park update libssl.so path Signed-off-by: Jinsoo Park Remove mkimage-unittest.sh Signed-off-by: Jinsoo Park --- contrib/mkimage-unittest.sh | 49 ------------------------------------- 1 file changed, 49 deletions(-) delete mode 100755 contrib/mkimage-unittest.sh diff --git a/contrib/mkimage-unittest.sh b/contrib/mkimage-unittest.sh deleted file mode 100755 index feebb17b0ef25..0000000000000 --- a/contrib/mkimage-unittest.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# Generate a very minimal filesystem based on busybox-static, -# and load it into the local docker under the name "docker-ut". - -missing_pkg() { - echo "Sorry, I could not locate $1" - echo "Try 'apt-get install ${2:-$1}'?" - exit 1 -} - -BUSYBOX=$(which busybox) -[ "$BUSYBOX" ] || missing_pkg busybox busybox-static -SOCAT=$(which socat) -[ "$SOCAT" ] || missing_pkg socat - -shopt -s extglob -set -ex -ROOTFS=`mktemp -d ${TMPDIR:-/var/tmp}/rootfs-busybox.XXXXXXXXXX` -trap "rm -rf $ROOTFS" INT QUIT TERM -cd $ROOTFS - -mkdir bin etc dev dev/pts lib proc sys tmp -touch etc/resolv.conf -cp /etc/nsswitch.conf etc/nsswitch.conf -echo root:x:0:0:root:/:/bin/sh > etc/passwd -echo daemon:x:1:1:daemon:/usr/sbin:/bin/sh >> etc/passwd -echo root:x:0: > etc/group -echo daemon:x:1: >> etc/group -ln -s lib lib64 -ln -s bin sbin -cp $BUSYBOX $SOCAT bin -for X in $(busybox --list) -do - ln -s busybox bin/$X -done -rm bin/init -ln bin/busybox bin/init -cp -P /lib/x86_64-linux-gnu/lib{pthread*,c*(-*),dl*(-*),nsl*(-*),nss_*,util*(-*),wrap,z}.so* lib -cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 lib -cp -P /usr/lib/x86_64-linux-gnu/lib{crypto,ssl}.so* lib -for X in console null ptmx random stdin stdout stderr tty urandom zero -do - cp -a /dev/$X dev -done - -chmod 0755 $ROOTFS # See #486 -tar --numeric-owner -cf- . | docker import - docker-ut -docker run -i -u root docker-ut /bin/echo Success. -rm -rf $ROOTFS From 50868b2c575c26492f4c49a79d2f3b51ec68a229 Mon Sep 17 00:00:00 2001 From: Daniel Antlinger Date: Thu, 23 Apr 2015 16:35:13 -0700 Subject: [PATCH 022/321] fixed TestDiffEnsureDockerinitFilesAreIgnored is too long #12672 Signed-off-by: Daniel Antlinger --- integration-cli/docker_cli_diff_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/integration-cli/docker_cli_diff_test.go b/integration-cli/docker_cli_diff_test.go index 332b128ed8f4c..725b762864a91 100644 --- a/integration-cli/docker_cli_diff_test.go +++ b/integration-cli/docker_cli_diff_test.go @@ -40,12 +40,14 @@ func (s *DockerSuite) TestDiffFilenameShownInOutput(c *check.C) { func (s *DockerSuite) TestDiffEnsureDockerinitFilesAreIgnored(c *check.C) { // this is a list of files which shouldn't show up in `docker diff` dockerinitFiles := []string{"/etc/resolv.conf", "/etc/hostname", "/etc/hosts", "/.dockerinit", "/.dockerenv"} + containerCount := 5 // we might not run into this problem from the first run, so start a few containers - for i := 0; i < 20; i++ { + for i := 0; i < containerCount; i++ { containerCmd := `echo foo > /root/bar` runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "sh", "-c", containerCmd) out, _, err := runCommandWithOutput(runCmd) + if err != nil { c.Fatal(out, err) } From 82daa43844556953101b201bc5983aed4fbe6233 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Fri, 10 Apr 2015 12:39:42 -0700 Subject: [PATCH 023/321] Fix for Daemon crashing when wildcards are used for COPY/ADD in the filename and the command itself Closes #12267 Signed-off-by: Doug Davis --- builder/internals.go | 9 +++++--- integration-cli/docker_cli_build_test.go | 27 +++++++++++++++++++++++- integration-cli/docker_utils.go | 1 - 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/builder/internals.go b/builder/internals.go index 09aca09f2bbd6..220cdf53db2eb 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -156,6 +156,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp dest, allowRemote, allowDecompression, + true, ); err != nil { return err } @@ -226,7 +227,7 @@ func (b *Builder) runContextCommand(args []string, allowRemote bool, allowDecomp return nil } -func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath string, destPath string, allowRemote bool, allowDecompression bool) error { +func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath string, destPath string, allowRemote bool, allowDecompression bool, allowWildcards bool) error { if origPath != "" && origPath[0] == '/' && len(origPath) > 1 { origPath = origPath[1:] @@ -351,7 +352,7 @@ func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath stri } // Deal with wildcards - if ContainsWildcards(origPath) { + if allowWildcards && ContainsWildcards(origPath) { for _, fileInfo := range b.context.GetSums() { if fileInfo.Name() == "" { continue @@ -361,7 +362,9 @@ func calcCopyInfo(b *Builder, cmdName string, cInfos *[]*copyInfo, origPath stri continue } - calcCopyInfo(b, cmdName, cInfos, fileInfo.Name(), destPath, allowRemote, allowDecompression) + // Note we set allowWildcards to false in case the name has + // a * in it + calcCopyInfo(b, cmdName, cInfos, fileInfo.Name(), destPath, allowRemote, allowDecompression, false) } return nil } diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 695e4cd6e6dd1..178f291a62e3a 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -1113,10 +1113,10 @@ func (s *DockerSuite) TestBuildCopyWildcard(c *check.C) { "dir/nested_dir/nest_nest_file": "2 times nested", "dirt": "dirty", }) - defer ctx.Close() if err != nil { c.Fatal(err) } + defer ctx.Close() id1, err := buildImageFromContext(name, ctx, true) if err != nil { @@ -1155,6 +1155,31 @@ func (s *DockerSuite) TestBuildCopyWildcardNoFind(c *check.C) { } +func (s *DockerSuite) TestBuildCopyWildcardInName(c *check.C) { + name := "testcopywildcardinname" + defer deleteImages(name) + ctx, err := fakeContext(`FROM busybox + COPY *.txt /tmp/ + RUN [ "$(cat /tmp/\*.txt)" = 'hi there' ] + `, map[string]string{"*.txt": "hi there"}) + + if err != nil { + // Normally we would do c.Fatal(err) here but given that + // the odds of this failing are so rare, it must be because + // the OS we're running the client on doesn't support * in + // filenames (like windows). So, instead of failing the test + // just let it pass. Then we don't need to explicitly + // say which OSs this works on or not. + return + } + defer ctx.Close() + + _, err = buildImageFromContext(name, ctx, true) + if err != nil { + c.Fatalf("should have built: %q", err) + } +} + func (s *DockerSuite) TestBuildCopyWildcardCache(c *check.C) { name := "testcopywildcardcache" ctx, err := fakeContext(`FROM busybox diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 5d2a537e1d6b8..25eecf07ac19a 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -663,7 +663,6 @@ func fakeContextAddDockerfile(ctx *FakeContext, dockerfile string) error { func fakeContext(dockerfile string, files map[string]string) (*FakeContext, error) { ctx, err := fakeContextWithFiles(files) if err != nil { - ctx.Close() return nil, err } if err := fakeContextAddDockerfile(ctx, dockerfile); err != nil { From a8e871b0bbbb63310f372332176875ffcc01aaf6 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Tue, 27 Jan 2015 07:57:34 -0800 Subject: [PATCH 024/321] Add support for Dockerfile CMD options This adds support for Dockerfile commands to have options - e.g: COPY --user=john foo /tmp/ COPY --ignore-mtime foo /tmp/ Supports both booleans and strings. Signed-off-by: Doug Davis --- builder/bflag.go | 155 ++++++++++++++++++ builder/bflag_test.go | 187 ++++++++++++++++++++++ builder/dispatchers.go | 16 ++ builder/evaluator.go | 6 +- builder/parser/parser.go | 4 +- builder/parser/testfiles/flags/Dockerfile | 10 ++ builder/parser/testfiles/flags/result | 10 ++ builder/parser/utils.go | 110 ++++++++++++- 8 files changed, 491 insertions(+), 7 deletions(-) create mode 100644 builder/bflag.go create mode 100644 builder/bflag_test.go create mode 100644 builder/parser/testfiles/flags/Dockerfile create mode 100644 builder/parser/testfiles/flags/result diff --git a/builder/bflag.go b/builder/bflag.go new file mode 100644 index 0000000000000..a6a2ba3a6f6a9 --- /dev/null +++ b/builder/bflag.go @@ -0,0 +1,155 @@ +package builder + +import ( + "fmt" + "strings" +) + +type FlagType int + +const ( + boolType FlagType = iota + stringType +) + +type BuilderFlags struct { + Args []string // actual flags/args from cmd line + flags map[string]*Flag + used map[string]*Flag + Err error +} + +type Flag struct { + bf *BuilderFlags + name string + flagType FlagType + Value string +} + +func NewBuilderFlags() *BuilderFlags { + return &BuilderFlags{ + flags: make(map[string]*Flag), + used: make(map[string]*Flag), + } +} + +func (bf *BuilderFlags) AddBool(name string, def bool) *Flag { + flag := bf.addFlag(name, boolType) + if flag == nil { + return nil + } + if def { + flag.Value = "true" + } else { + flag.Value = "false" + } + return flag +} + +func (bf *BuilderFlags) AddString(name string, def string) *Flag { + flag := bf.addFlag(name, stringType) + if flag == nil { + return nil + } + flag.Value = def + return flag +} + +func (bf *BuilderFlags) addFlag(name string, flagType FlagType) *Flag { + if _, ok := bf.flags[name]; ok { + bf.Err = fmt.Errorf("Duplicate flag defined: %s", name) + return nil + } + + newFlag := &Flag{ + bf: bf, + name: name, + flagType: flagType, + } + bf.flags[name] = newFlag + + return newFlag +} + +func (fl *Flag) IsUsed() bool { + if _, ok := fl.bf.used[fl.name]; ok { + return true + } + return false +} + +func (fl *Flag) IsTrue() bool { + if fl.flagType != boolType { + // Should never get here + panic(fmt.Errorf("Trying to use IsTrue on a non-boolean: %s", fl.name)) + } + return fl.Value == "true" +} + +func (bf *BuilderFlags) Parse() error { + // If there was an error while defining the possible flags + // go ahead and bubble it back up here since we didn't do it + // earlier in the processing + if bf.Err != nil { + return fmt.Errorf("Error setting up flags: %s", bf.Err) + } + + for _, arg := range bf.Args { + if !strings.HasPrefix(arg, "--") { + return fmt.Errorf("Arg should start with -- : %s", arg) + } + + if arg == "--" { + return nil + } + + arg = arg[2:] + value := "" + + index := strings.Index(arg, "=") + if index >= 0 { + value = arg[index+1:] + arg = arg[:index] + } + + flag, ok := bf.flags[arg] + if !ok { + return fmt.Errorf("Unknown flag: %s", arg) + } + + if _, ok = bf.used[arg]; ok { + return fmt.Errorf("Duplicate flag specified: %s", arg) + } + + bf.used[arg] = flag + + switch flag.flagType { + case boolType: + // value == "" is only ok if no "=" was specified + if index >= 0 && value == "" { + return fmt.Errorf("Missing a value on flag: %s", arg) + } + + lower := strings.ToLower(value) + if lower == "" { + flag.Value = "true" + } else if lower == "true" || lower == "false" { + flag.Value = lower + } else { + return fmt.Errorf("Expecting boolean value for flag %s, not: %s", arg, value) + } + + case stringType: + if index < 0 { + return fmt.Errorf("Missing a value on flag: %s", arg) + } + flag.Value = value + + default: + panic(fmt.Errorf("No idea what kind of flag we have! Should never get here!")) + } + + } + + return nil +} diff --git a/builder/bflag_test.go b/builder/bflag_test.go new file mode 100644 index 0000000000000..d03a1c30651cb --- /dev/null +++ b/builder/bflag_test.go @@ -0,0 +1,187 @@ +package builder + +import ( + "testing" +) + +func TestBuilderFlags(t *testing.T) { + var expected string + var err error + + // --- + + bf := NewBuilderFlags() + bf.Args = []string{} + if err := bf.Parse(); err != nil { + t.Fatalf("Test1 of %q was supposed to work: %s", bf.Args, err) + } + + // --- + + bf = NewBuilderFlags() + bf.Args = []string{"--"} + if err := bf.Parse(); err != nil { + t.Fatalf("Test2 of %q was supposed to work: %s", bf.Args, err) + } + + // --- + + bf = NewBuilderFlags() + flStr1 := bf.AddString("str1", "") + flBool1 := bf.AddBool("bool1", false) + bf.Args = []string{} + if err = bf.Parse(); err != nil { + t.Fatalf("Test3 of %q was supposed to work: %s", bf.Args, err) + } + + if flStr1.IsUsed() == true { + t.Fatalf("Test3 - str1 was not used!") + } + if flBool1.IsUsed() == true { + t.Fatalf("Test3 - bool1 was not used!") + } + + // --- + + bf = NewBuilderFlags() + flStr1 = bf.AddString("str1", "HI") + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test4 of %q was supposed to work: %s", bf.Args, err) + } + + if flStr1.Value != "HI" { + t.Fatalf("Str1 was supposed to default to: HI") + } + if flBool1.IsTrue() { + t.Fatalf("Bool1 was supposed to default to: false") + } + if flStr1.IsUsed() == true { + t.Fatalf("Str1 was not used!") + } + if flBool1.IsUsed() == true { + t.Fatalf("Bool1 was not used!") + } + + // --- + + bf = NewBuilderFlags() + flStr1 = bf.AddString("str1", "HI") + bf.Args = []string{"--str1"} + + if err = bf.Parse(); err == nil { + t.Fatalf("Test %q was supposed to fail", bf.Args) + } + + // --- + + bf = NewBuilderFlags() + flStr1 = bf.AddString("str1", "HI") + bf.Args = []string{"--str1="} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test %q was supposed to work: %s", bf.Args, err) + } + + expected = "" + if flStr1.Value != expected { + t.Fatalf("Str1 (%q) should be: %q", flStr1.Value, expected) + } + + // --- + + bf = NewBuilderFlags() + flStr1 = bf.AddString("str1", "HI") + bf.Args = []string{"--str1=BYE"} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test %q was supposed to work: %s", bf.Args, err) + } + + expected = "BYE" + if flStr1.Value != expected { + t.Fatalf("Str1 (%q) should be: %q", flStr1.Value, expected) + } + + // --- + + bf = NewBuilderFlags() + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{"--bool1"} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test %q was supposed to work: %s", bf.Args, err) + } + + if !flBool1.IsTrue() { + t.Fatalf("Test-b1 Bool1 was supposed to be true") + } + + // --- + + bf = NewBuilderFlags() + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{"--bool1=true"} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test %q was supposed to work: %s", bf.Args, err) + } + + if !flBool1.IsTrue() { + t.Fatalf("Test-b2 Bool1 was supposed to be true") + } + + // --- + + bf = NewBuilderFlags() + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{"--bool1=false"} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test %q was supposed to work: %s", bf.Args, err) + } + + if flBool1.IsTrue() { + t.Fatalf("Test-b3 Bool1 was supposed to be false") + } + + // --- + + bf = NewBuilderFlags() + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{"--bool1=false1"} + + if err = bf.Parse(); err == nil { + t.Fatalf("Test %q was supposed to fail", bf.Args) + } + + // --- + + bf = NewBuilderFlags() + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{"--bool2"} + + if err = bf.Parse(); err == nil { + t.Fatalf("Test %q was supposed to fail", bf.Args) + } + + // --- + + bf = NewBuilderFlags() + flStr1 = bf.AddString("str1", "HI") + flBool1 = bf.AddBool("bool1", false) + bf.Args = []string{"--bool1", "--str1=BYE"} + + if err = bf.Parse(); err != nil { + t.Fatalf("Test %q was supposed to work: %s", bf.Args, err) + } + + if flStr1.Value != "BYE" { + t.Fatalf("Teset %s, str1 should be BYE", bf.Args) + } + if !flBool1.IsTrue() { + t.Fatalf("Teset %s, bool1 should be true", bf.Args) + } +} diff --git a/builder/dispatchers.go b/builder/dispatchers.go index e807f1aee1034..195d18305d03c 100644 --- a/builder/dispatchers.go +++ b/builder/dispatchers.go @@ -47,6 +47,22 @@ func env(b *Builder, args []string, attributes map[string]bool, original string) return fmt.Errorf("Bad input to ENV, too many args") } + // TODO/FIXME/NOT USED + // Just here to show how to use the builder flags stuff within the + // context of a builder command. Will remove once we actually add + // a builder command to something! + /* + flBool1 := b.BuilderFlags.AddBool("bool1", false) + flStr1 := b.BuilderFlags.AddString("str1", "HI") + + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + + fmt.Printf("Bool1:%v\n", flBool1) + fmt.Printf("Str1:%v\n", flStr1) + */ + commitStr := "ENV" for j := 0; j < len(args); j++ { diff --git a/builder/evaluator.go b/builder/evaluator.go index 9a2b57a8f93ea..7dfb001bd8c66 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -116,6 +116,7 @@ type Builder struct { image string // image name for commit processing maintainer string // maintainer name. could probably be removed. cmdSet bool // indicates is CMD was set in current Dockerfile + BuilderFlags *BuilderFlags // current cmd's BuilderFlags - temporary context tarsum.TarSum // the context is a tarball that is uploaded by the client contextPath string // the path of the temporary directory the local context is unpacked to (server side) noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system. @@ -276,8 +277,9 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error { cmd := ast.Value attrs := ast.Attributes original := ast.Original + flags := ast.Flags strs := []string{} - msg := fmt.Sprintf("Step %d : %s", stepN, strings.ToUpper(cmd)) + msg := fmt.Sprintf("Step %d : %s", stepN, original) if cmd == "onbuild" { if ast.Next == nil { @@ -325,6 +327,8 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error { // XXX yes, we skip any cmds that are not valid; the parser should have // picked these out already. if f, ok := evaluateTable[cmd]; ok { + b.BuilderFlags = NewBuilderFlags() + b.BuilderFlags.Args = flags return f(b, strList, attrs, original) } diff --git a/builder/parser/parser.go b/builder/parser/parser.go index 1ab151b30df67..f68c710c06e39 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -29,6 +29,7 @@ type Node struct { Children []*Node // the children of this sexp Attributes map[string]bool // special attributes for this node Original string // original line used before parsing + Flags []string // only top Node should have this set } var ( @@ -75,7 +76,7 @@ func parseLine(line string) (string, *Node, error) { return line, nil, nil } - cmd, args, err := splitCommand(line) + cmd, flags, args, err := splitCommand(line) if err != nil { return "", nil, err } @@ -91,6 +92,7 @@ func parseLine(line string) (string, *Node, error) { node.Next = sexp node.Attributes = attrs node.Original = line + node.Flags = flags return "", node, nil } diff --git a/builder/parser/testfiles/flags/Dockerfile b/builder/parser/testfiles/flags/Dockerfile new file mode 100644 index 0000000000000..2418e0f069b9a --- /dev/null +++ b/builder/parser/testfiles/flags/Dockerfile @@ -0,0 +1,10 @@ +FROM scratch +COPY foo /tmp/ +COPY --user=me foo /tmp/ +COPY --doit=true foo /tmp/ +COPY --user=me --doit=true foo /tmp/ +COPY --doit=true -- foo /tmp/ +COPY -- foo /tmp/ +CMD --doit [ "a", "b" ] +CMD --doit=true -- [ "a", "b" ] +CMD --doit -- [ ] diff --git a/builder/parser/testfiles/flags/result b/builder/parser/testfiles/flags/result new file mode 100644 index 0000000000000..4578f4cba4b8c --- /dev/null +++ b/builder/parser/testfiles/flags/result @@ -0,0 +1,10 @@ +(from "scratch") +(copy "foo" "/tmp/") +(copy ["--user=me"] "foo" "/tmp/") +(copy ["--doit=true"] "foo" "/tmp/") +(copy ["--user=me" "--doit=true"] "foo" "/tmp/") +(copy ["--doit=true"] "foo" "/tmp/") +(copy "foo" "/tmp/") +(cmd ["--doit"] "a" "b") +(cmd ["--doit=true"] "a" "b") +(cmd ["--doit"]) diff --git a/builder/parser/utils.go b/builder/parser/utils.go index a60ad129fe85a..5d82e9604ec0e 100644 --- a/builder/parser/utils.go +++ b/builder/parser/utils.go @@ -1,8 +1,10 @@ package parser import ( + "fmt" "strconv" "strings" + "unicode" ) // dumps the AST defined by `node` as a list of sexps. Returns a string @@ -11,6 +13,10 @@ func (node *Node) Dump() string { str := "" str += node.Value + if len(node.Flags) > 0 { + str += fmt.Sprintf(" %q", node.Flags) + } + for _, n := range node.Children { str += "(" + n.Dump() + ")\n" } @@ -48,20 +54,23 @@ func fullDispatch(cmd, args string) (*Node, map[string]bool, error) { // splitCommand takes a single line of text and parses out the cmd and args, // which are used for dispatching to more exact parsing functions. -func splitCommand(line string) (string, string, error) { +func splitCommand(line string) (string, []string, string, error) { var args string + var flags []string // Make sure we get the same results irrespective of leading/trailing spaces cmdline := TOKEN_WHITESPACE.Split(strings.TrimSpace(line), 2) cmd := strings.ToLower(cmdline[0]) if len(cmdline) == 2 { - args = strings.TrimSpace(cmdline[1]) + var err error + args, flags, err = extractBuilderFlags(cmdline[1]) + if err != nil { + return "", nil, "", err + } } - // the cmd should never have whitespace, but it's possible for the args to - // have trailing whitespace. - return cmd, args, nil + return cmd, flags, strings.TrimSpace(args), nil } // covers comments and empty lines. Lines should be trimmed before passing to @@ -74,3 +83,94 @@ func stripComments(line string) string { return line } + +func extractBuilderFlags(line string) (string, []string, error) { + // Parses the BuilderFlags and returns the remaining part of the line + + const ( + inSpaces = iota // looking for start of a word + inWord + inQuote + ) + + words := []string{} + phase := inSpaces + word := "" + quote := '\000' + blankOK := false + var ch rune + + for pos := 0; pos <= len(line); pos++ { + if pos != len(line) { + ch = rune(line[pos]) + } + + if phase == inSpaces { // Looking for start of word + if pos == len(line) { // end of input + break + } + if unicode.IsSpace(ch) { // skip spaces + continue + } + + // Only keep going if the next word starts with -- + if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' { + return line[pos:], words, nil + } + + phase = inWord // found someting with "--", fall thru + } + if (phase == inWord || phase == inQuote) && (pos == len(line)) { + if word != "--" && (blankOK || len(word) > 0) { + words = append(words, word) + } + break + } + if phase == inWord { + if unicode.IsSpace(ch) { + phase = inSpaces + if word == "--" { + return line[pos:], words, nil + } + if blankOK || len(word) > 0 { + words = append(words, word) + } + word = "" + blankOK = false + continue + } + if ch == '\'' || ch == '"' { + quote = ch + blankOK = true + phase = inQuote + continue + } + if ch == '\\' { + if pos+1 == len(line) { + continue // just skip \ at end + } + pos++ + ch = rune(line[pos]) + } + word += string(ch) + continue + } + if phase == inQuote { + if ch == quote { + phase = inWord + continue + } + if ch == '\\' { + if pos+1 == len(line) { + phase = inWord + continue // just skip \ at end + } + pos++ + ch = rune(line[pos]) + } + word += string(ch) + } + } + + return "", words, nil +} From f04837cf803583dfeb4dcd8311e4e5ef8764c37c Mon Sep 17 00:00:00 2001 From: nikolas Date: Tue, 28 Apr 2015 13:25:48 -0400 Subject: [PATCH 025/321] Remove incorrect option in docker install command The `-N` option is not compatible with the `-O` option of wget (see the man page). The example command now matches the example in the script on http://get.docker.com/. Signed-off-by: Nik Nyby --- docs/sources/installation/ubuntulinux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/installation/ubuntulinux.md b/docs/sources/installation/ubuntulinux.md index 75b3c9fb68337..6c854997fc253 100644 --- a/docs/sources/installation/ubuntulinux.md +++ b/docs/sources/installation/ubuntulinux.md @@ -308,5 +308,5 @@ NetworkManager (this might slow your network). To install the latest version of Docker, use the standard `-N` flag with `wget`: - $ wget -N -qO- https://get.docker.com/ | sh + $ wget -qO- https://get.docker.com/ | sh From 8a2f8992865a706df30eb23bd861444a5ecf6198 Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Tue, 28 Apr 2015 16:05:28 +0800 Subject: [PATCH 026/321] use CustomSize replace intToString Signed-off-by: Ma Shimiao --- pkg/units/size.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pkg/units/size.go b/pkg/units/size.go index 7cfb57ba51fed..d7850ad0b0c5b 100644 --- a/pkg/units/size.go +++ b/pkg/units/size.go @@ -37,23 +37,25 @@ var ( var decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} var binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} +// CustomSize returns a human-readable approximation of a size +// using custom format +func CustomSize(format string, size float64, base float64, _map []string) string { + i := 0 + for size >= base { + size = size / base + i++ + } + return fmt.Sprintf(format, size, _map[i]) +} + // HumanSize returns a human-readable approximation of a size // using SI standard (eg. "44kB", "17MB") func HumanSize(size float64) string { - return intToString(float64(size), 1000.0, decimapAbbrs) + return CustomSize("%.4g %s", float64(size), 1000.0, decimapAbbrs) } func BytesSize(size float64) string { - return intToString(size, 1024.0, binaryAbbrs) -} - -func intToString(size, unit float64, _map []string) string { - i := 0 - for size >= unit { - size = size / unit - i++ - } - return fmt.Sprintf("%.4g %s", size, _map[i]) + return CustomSize("%.4g %s", size, 1024.0, binaryAbbrs) } // FromHumanSize returns an integer from a human-readable specification of a From 534ed8c2d4573e88fcb68b23341504f8949b34b5 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Tue, 31 Mar 2015 13:49:41 -0700 Subject: [PATCH 027/321] Remove use of "DEBUG" env var from CLI and decouple DEBUG from --log-level Signed-off-by: Doug Davis --- api/client/info.go | 4 +--- docker/docker.go | 3 --- integration-cli/docker_cli_daemon_test.go | 12 ++++++------ 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/api/client/info.go b/api/client/info.go index 432ccac40fcce..06a6f0ec54af2 100644 --- a/api/client/info.go +++ b/api/client/info.go @@ -3,7 +3,6 @@ package client import ( "encoding/json" "fmt" - "os" "github.com/docker/docker/api/types" flag "github.com/docker/docker/pkg/mflag" @@ -45,9 +44,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error { fmt.Fprintf(cli.out, "Name: %s\n", info.Name) fmt.Fprintf(cli.out, "ID: %s\n", info.ID) - if info.Debug || os.Getenv("DEBUG") != "" { + if info.Debug { fmt.Fprintf(cli.out, "Debug mode (server): %v\n", info.Debug) - fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "") fmt.Fprintf(cli.out, "File Descriptors: %d\n", info.NFd) fmt.Fprintf(cli.out, "Goroutines: %d\n", info.NGoroutines) fmt.Fprintf(cli.out, "System Time: %s\n", info.SystemTime) diff --git a/docker/docker.go b/docker/docker.go index 1096b840f8468..0fc08ad7f9613 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -52,11 +52,8 @@ func main() { setLogLevel(logrus.InfoLevel) } - // -D, --debug, -l/--log-level=debug processing - // When/if -D is removed this block can be deleted if *flDebug { os.Setenv("DEBUG", "1") - setLogLevel(logrus.DebugLevel) } if len(flHosts) == 0 { diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index e099995ad3aed..98bcd1ad21cc8 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -229,8 +229,8 @@ func (s *DockerDaemonSuite) TestDaemonFlagD(c *check.C) { c.Fatal(err) } content, _ := ioutil.ReadFile(s.d.logFile.Name()) - if !strings.Contains(string(content), `level=debug`) { - c.Fatalf(`Missing level="debug" in log file using -D:\n%s`, string(content)) + if strings.Contains(string(content), `level=debug`) { + c.Fatalf(`Should not have level="debug" in log file using -D:\n%s`, string(content)) } } @@ -239,8 +239,8 @@ func (s *DockerDaemonSuite) TestDaemonFlagDebug(c *check.C) { c.Fatal(err) } content, _ := ioutil.ReadFile(s.d.logFile.Name()) - if !strings.Contains(string(content), `level=debug`) { - c.Fatalf(`Missing level="debug" in log file using --debug:\n%s`, string(content)) + if strings.Contains(string(content), `level=debug`) { + c.Fatalf(`Should not have level="debug" in log file using --debug:\n%s`, string(content)) } } @@ -249,8 +249,8 @@ func (s *DockerDaemonSuite) TestDaemonFlagDebugLogLevelFatal(c *check.C) { c.Fatal(err) } content, _ := ioutil.ReadFile(s.d.logFile.Name()) - if !strings.Contains(string(content), `level=debug`) { - c.Fatalf(`Missing level="debug" in log file when using both --debug and --log-level=fatal:\n%s`, string(content)) + if strings.Contains(string(content), `level=debug`) { + c.Fatalf(`Should not have level="debug" in log file when using both --debug and --log-level=fatal:\n%s`, string(content)) } } From f3f5ff9d837eecb97eeeb878f0bd416b6ab57cf2 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Mon, 27 Apr 2015 13:16:33 -0700 Subject: [PATCH 028/321] Integration tests for --bridge daemon flag Signed-off-by: Madhu Venugopal --- integration-cli/docker_cli_daemon_test.go | 41 +++++++++++++++++++++++ integration-cli/docker_utils.go | 9 +++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index e099995ad3aed..3ebc880a0c8c9 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net" "os" "os/exec" "path/filepath" @@ -447,6 +448,46 @@ func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *check.C) { } } +func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { + d := s.d + err := d.Start("--bridge", "nosuchbridge") + c.Assert(err, check.Not(check.IsNil), check.Commentf("--bridge option with an invalid bridge should cause the daemon to fail")) + + bridgeName := "external-bridge" + bridgeIp := "192.169.1.1/24" + _, bridgeIPNet, _ := net.ParseCIDR(bridgeIp) + + args := []string{"link", "add", "name", bridgeName, "type", "bridge"} + ipLinkCmd := exec.Command("ip", args...) + _, _, _, err = runCommandWithStdoutStderr(ipLinkCmd) + c.Assert(err, check.IsNil) + + ifCfgCmd := exec.Command("ifconfig", bridgeName, bridgeIp, "up") + _, _, _, err = runCommandWithStdoutStderr(ifCfgCmd) + c.Assert(err, check.IsNil) + + err = d.StartWithBusybox("--bridge", bridgeName) + c.Assert(err, check.IsNil) + + ipTablesSearchString := bridgeIPNet.String() + ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL") + out, _, err := runCommandWithOutput(ipTablesCmd) + c.Assert(err, check.IsNil) + + c.Assert(strings.Contains(out, ipTablesSearchString), check.Equals, true, + check.Commentf("iptables output should have contained %q, but was %q", + ipTablesSearchString, out)) + + _, err = d.Cmd("run", "-d", "--name", "ExtContainer", "busybox", "top") + c.Assert(err, check.IsNil) + + containerIp := d.findContainerIP(c, "ExtContainer") + ip := net.ParseIP(containerIp) + c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, + check.Commentf("Container IP-Address must be in the same subnet range : %s", + containerIp)) +} + func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { testRequires(c, NativeExecDriver) diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 8386bb59ff962..a29b6c5928bda 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -570,8 +570,9 @@ func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...strin return out, status, err } -func findContainerIP(c *check.C, id string) string { - cmd := exec.Command(dockerBinary, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id) +func findContainerIP(c *check.C, id string, vargs ...string) string { + args := append(vargs, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id) + cmd := exec.Command(dockerBinary, args...) out, _, err := runCommandWithOutput(cmd) if err != nil { c.Fatal(err, out) @@ -580,6 +581,10 @@ func findContainerIP(c *check.C, id string) string { return strings.Trim(out, " \r\n'") } +func (d *Daemon) findContainerIP(c *check.C, id string) string { + return findContainerIP(c, id, "--host", d.sock()) +} + func getContainerCount() (int, error) { const containers = "Containers:" From 9c325c3f54b24621b76dee530a855b37cb22abcc Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Mon, 27 Apr 2015 20:36:40 -0700 Subject: [PATCH 029/321] Integration tests for --bip daemon flag Signed-off-by: Madhu Venugopal --- integration-cli/docker_cli_daemon_test.go | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 3ebc880a0c8c9..7acbe4640f64b 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -488,6 +488,71 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { containerIp)) } +func deleteBridge(c *check.C, bridge string) { + ifCmd := exec.Command("ip", "link", "delete", bridge) + _, _, _, err := runCommandWithStdoutStderr(ifCmd) + c.Assert(err, check.IsNil) + + flushCmd := exec.Command("iptables", "-t", "nat", "--flush") + _, _, _, err = runCommandWithStdoutStderr(flushCmd) + c.Assert(err, check.IsNil) +} + +func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { + // TestDaemonBridgeIP Steps + // 1. Delete the existing docker0 Bridge + // 2. Set --bip daemon configuration and start the new Docker Daemon + // 3. Check if the bip config has taken effect using ifconfig and iptables commands + // 4. Launch a Container and make sure the IP-Address is in the expected subnet + // 5. Delete the docker0 Bridge + // 6. Restart the Docker Daemon (with no --bip settings) + // This Restart takes care of bringing docker0 interface back to auto-assigned IP + // 7. Stop the Docker Daemon (via defered action) + + defaultNetworkBridge := "docker0" + deleteBridge(c, defaultNetworkBridge) + + d := s.d + + bridgeIp := "192.169.1.1/24" + ip, bridgeIPNet, _ := net.ParseCIDR(bridgeIp) + + err := d.StartWithBusybox("--bip", bridgeIp) + c.Assert(err, check.IsNil) + + ifconfigSearchString := ip.String() + ifconfigCmd := exec.Command("ifconfig", defaultNetworkBridge) + out, _, _, err := runCommandWithStdoutStderr(ifconfigCmd) + c.Assert(err, check.IsNil) + + c.Assert(strings.Contains(out, ifconfigSearchString), check.Equals, true, + check.Commentf("ifconfig output should have contained %q, but was %q", + ifconfigSearchString, out)) + + ipTablesSearchString := bridgeIPNet.String() + ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL") + out, _, err = runCommandWithOutput(ipTablesCmd) + c.Assert(err, check.IsNil) + + c.Assert(strings.Contains(out, ipTablesSearchString), check.Equals, true, + check.Commentf("iptables output should have contained %q, but was %q", + ipTablesSearchString, out)) + + out, err = d.Cmd("run", "-d", "--name", "test", "busybox", "top") + c.Assert(err, check.IsNil) + + containerIp := d.findContainerIP(c, "test") + ip = net.ParseIP(containerIp) + c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, + check.Commentf("Container IP-Address must be in the same subnet range : %s", + containerIp)) + + // Reset to Defaults + deleteBridge(c, defaultNetworkBridge) + d.Restart() + pingContainers(c) +} + func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { testRequires(c, NativeExecDriver) @@ -930,3 +995,16 @@ func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) { c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out) } } + +func pingContainers(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "container1", + "--hostname", "fred", "busybox", "top") + _, err := runCommand(runCmd) + c.Assert(err, check.IsNil) + + runArgs := []string{"run", "--rm", "--link", "container1:alias1", "busybox", "sh", "-c"} + pingCmd := "ping -c 1 %s -W 1" + + dockerCmd(c, append(runArgs, fmt.Sprintf(pingCmd, "alias1"))...) + dockerCmd(c, "rm", "-f", "container1") +} From 0e254411b1fe0b5024d4a8e5ade7ce12f4545d8e Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Tue, 28 Apr 2015 08:55:04 -0700 Subject: [PATCH 030/321] Integration tests for --fixed-cidr daemon config Signed-off-by: Madhu Venugopal --- integration-cli/docker_cli_daemon_test.go | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 7acbe4640f64b..3dfcdfdb34777 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" "time" @@ -486,6 +487,10 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, check.Commentf("Container IP-Address must be in the same subnet range : %s", containerIp)) + + // Reset to Defaults + deleteBridge(c, bridgeName) + d.Restart() } func deleteBridge(c *check.C, bridge string) { @@ -553,6 +558,37 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { pingContainers(c) } +func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { + d := s.d + + bridgeName := "external-bridge" + args := []string{"link", "add", "name", bridgeName, "type", "bridge"} + ipLinkCmd := exec.Command("ip", args...) + _, _, _, err := runCommandWithStdoutStderr(ipLinkCmd) + c.Assert(err, check.IsNil) + + ifCmd := exec.Command("ifconfig", bridgeName, "192.169.1.1/24", "up") + _, _, _, err = runCommandWithStdoutStderr(ifCmd) + c.Assert(err, check.IsNil) + + args = []string{"--bridge", bridgeName, "--fixed-cidr", "192.169.1.0/30"} + err = d.StartWithBusybox(args...) + c.Assert(err, check.IsNil) + + for i := 0; i < 4; i++ { + cName := "Container" + strconv.Itoa(i) + out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top") + if err != nil { + c.Assert(strings.Contains(out, "no available ip addresses"), check.Equals, true, + check.Commentf("Could not run a Container : %s %s", err.Error(), out)) + } + } + + // Reset to Defaults + deleteBridge(c, bridgeName) + d.Restart() +} + func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { testRequires(c, NativeExecDriver) From ba11929ebdf4cf7798cddc98c4dcfc000b154264 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Tue, 28 Apr 2015 10:26:59 -0700 Subject: [PATCH 031/321] Integration tests for --ip daemon option Signed-off-by: Madhu Venugopal --- integration-cli/docker_cli_daemon_test.go | 52 ++++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 3dfcdfdb34777..5c5fdbd57175b 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strconv" "strings" "time" @@ -489,11 +490,11 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { containerIp)) // Reset to Defaults - deleteBridge(c, bridgeName) + deleteInterface(c, bridgeName) d.Restart() } -func deleteBridge(c *check.C, bridge string) { +func deleteInterface(c *check.C, bridge string) { ifCmd := exec.Command("ip", "link", "delete", bridge) _, _, _, err := runCommandWithStdoutStderr(ifCmd) c.Assert(err, check.IsNil) @@ -515,7 +516,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { // 7. Stop the Docker Daemon (via defered action) defaultNetworkBridge := "docker0" - deleteBridge(c, defaultNetworkBridge) + deleteInterface(c, defaultNetworkBridge) d := s.d @@ -553,7 +554,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { containerIp)) // Reset to Defaults - deleteBridge(c, defaultNetworkBridge) + deleteInterface(c, defaultNetworkBridge) d.Restart() pingContainers(c) } @@ -585,7 +586,48 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { } // Reset to Defaults - deleteBridge(c, bridgeName) + deleteInterface(c, bridgeName) + d.Restart() +} + +func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { + d := s.d + + ipStr := "192.170.1.1/24" + ip, _, _ := net.ParseCIDR(ipStr) + args := []string{"--ip", ip.String()} + err := d.StartWithBusybox(args...) + c.Assert(err, check.IsNil) + + out, err := d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") + c.Assert(err, check.Not(check.IsNil), + check.Commentf("Running a container must fail with an invalid --ip option")) + c.Assert(strings.Contains(out, "Error starting userland proxy"), check.Equals, true) + + ifName := "dummy" + args = []string{"link", "add", "name", ifName, "type", "dummy"} + ipLinkCmd := exec.Command("ip", args...) + _, _, _, err = runCommandWithStdoutStderr(ipLinkCmd) + c.Assert(err, check.IsNil) + + ifCmd := exec.Command("ifconfig", ifName, ipStr, "up") + _, _, _, err = runCommandWithStdoutStderr(ifCmd) + c.Assert(err, check.IsNil) + + _, err = d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") + c.Assert(err, check.IsNil) + + ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL") + out, _, err = runCommandWithOutput(ipTablesCmd) + c.Assert(err, check.IsNil) + + regex := fmt.Sprintf("DNAT.*%s.*dpt:8000", ip.String()) + matched, _ := regexp.MatchString(regex, out) + c.Assert(matched, check.Equals, true, + check.Commentf("iptables output should have contained %q, but was %q", regex, out)) + + // Reset to Defaults + deleteInterface(c, ifName) d.Restart() } From dd0666e64f17329355c77aae1a2ac0fe2fe43402 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Tue, 28 Apr 2015 16:17:00 -0700 Subject: [PATCH 032/321] Integration Tests for --icc=false & container Linking using --expose Signed-off-by: Madhu Venugopal --- integration-cli/docker_cli_daemon_test.go | 163 ++++++++++++++++------ integration-cli/docker_utils.go | 4 +- 2 files changed, 124 insertions(+), 43 deletions(-) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 5c5fdbd57175b..738d9b0aa2474 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -453,20 +453,13 @@ func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *check.C) { func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { d := s.d err := d.Start("--bridge", "nosuchbridge") - c.Assert(err, check.Not(check.IsNil), check.Commentf("--bridge option with an invalid bridge should cause the daemon to fail")) + c.Assert(err, check.NotNil, check.Commentf("--bridge option with an invalid bridge should cause the daemon to fail")) bridgeName := "external-bridge" bridgeIp := "192.169.1.1/24" _, bridgeIPNet, _ := net.ParseCIDR(bridgeIp) - args := []string{"link", "add", "name", bridgeName, "type", "bridge"} - ipLinkCmd := exec.Command("ip", args...) - _, _, _, err = runCommandWithStdoutStderr(ipLinkCmd) - c.Assert(err, check.IsNil) - - ifCfgCmd := exec.Command("ifconfig", bridgeName, bridgeIp, "up") - _, _, _, err = runCommandWithStdoutStderr(ifCfgCmd) - c.Assert(err, check.IsNil) + createInterface(c, "bridge", bridgeName, bridgeIp) err = d.StartWithBusybox("--bridge", bridgeName) c.Assert(err, check.IsNil) @@ -483,7 +476,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { _, err = d.Cmd("run", "-d", "--name", "ExtContainer", "busybox", "top") c.Assert(err, check.IsNil) - containerIp := d.findContainerIP(c, "ExtContainer") + containerIp := d.findContainerIP("ExtContainer") ip := net.ParseIP(containerIp) c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, check.Commentf("Container IP-Address must be in the same subnet range : %s", @@ -494,14 +487,29 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { d.Restart() } +func createInterface(c *check.C, ifType string, ifName string, ipNet string) { + args := []string{"link", "add", "name", ifName, "type", ifType} + ipLinkCmd := exec.Command("ip", args...) + out, _, err := runCommandWithOutput(ipLinkCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + + ifCfgCmd := exec.Command("ifconfig", ifName, ipNet, "up") + out, _, err = runCommandWithOutput(ifCfgCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) +} + func deleteInterface(c *check.C, bridge string) { ifCmd := exec.Command("ip", "link", "delete", bridge) - _, _, _, err := runCommandWithStdoutStderr(ifCmd) - c.Assert(err, check.IsNil) + out, _, err := runCommandWithOutput(ifCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) flushCmd := exec.Command("iptables", "-t", "nat", "--flush") - _, _, _, err = runCommandWithStdoutStderr(flushCmd) - c.Assert(err, check.IsNil) + out, _, err = runCommandWithOutput(flushCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + + flushCmd = exec.Command("iptables", "--flush") + out, _, err = runCommandWithOutput(flushCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) } func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { @@ -547,7 +555,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { out, err = d.Cmd("run", "-d", "--name", "test", "busybox", "top") c.Assert(err, check.IsNil) - containerIp := d.findContainerIP(c, "test") + containerIp := d.findContainerIP("test") ip = net.ParseIP(containerIp) c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, check.Commentf("Container IP-Address must be in the same subnet range : %s", @@ -556,24 +564,19 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { // Reset to Defaults deleteInterface(c, defaultNetworkBridge) d.Restart() - pingContainers(c) + pingContainers(c, nil, false) } func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { d := s.d bridgeName := "external-bridge" - args := []string{"link", "add", "name", bridgeName, "type", "bridge"} - ipLinkCmd := exec.Command("ip", args...) - _, _, _, err := runCommandWithStdoutStderr(ipLinkCmd) - c.Assert(err, check.IsNil) + bridgeIp := "192.169.1.1/24" - ifCmd := exec.Command("ifconfig", bridgeName, "192.169.1.1/24", "up") - _, _, _, err = runCommandWithStdoutStderr(ifCmd) - c.Assert(err, check.IsNil) + createInterface(c, "bridge", bridgeName, bridgeIp) - args = []string{"--bridge", bridgeName, "--fixed-cidr", "192.169.1.0/30"} - err = d.StartWithBusybox(args...) + args := []string{"--bridge", bridgeName, "--fixed-cidr", "192.169.1.0/30"} + err := d.StartWithBusybox(args...) c.Assert(err, check.IsNil) for i := 0; i < 4; i++ { @@ -600,19 +603,12 @@ func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { c.Assert(err, check.IsNil) out, err := d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") - c.Assert(err, check.Not(check.IsNil), + c.Assert(err, check.NotNil, check.Commentf("Running a container must fail with an invalid --ip option")) c.Assert(strings.Contains(out, "Error starting userland proxy"), check.Equals, true) ifName := "dummy" - args = []string{"link", "add", "name", ifName, "type", "dummy"} - ipLinkCmd := exec.Command("ip", args...) - _, _, _, err = runCommandWithStdoutStderr(ipLinkCmd) - c.Assert(err, check.IsNil) - - ifCmd := exec.Command("ifconfig", ifName, ipStr, "up") - _, _, _, err = runCommandWithStdoutStderr(ifCmd) - c.Assert(err, check.IsNil) + createInterface(c, "dummy", ifName, ipStr) _, err = d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") c.Assert(err, check.IsNil) @@ -631,6 +627,79 @@ func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { d.Restart() } +func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) { + d := s.d + + bridgeName := "external-bridge" + bridgeIp := "192.169.1.1/24" + + createInterface(c, "bridge", bridgeName, bridgeIp) + + args := []string{"--bridge", bridgeName, "--icc=false"} + err := d.StartWithBusybox(args...) + c.Assert(err, check.IsNil) + + ipTablesCmd := exec.Command("iptables", "-nvL", "FORWARD") + out, _, err := runCommandWithOutput(ipTablesCmd) + c.Assert(err, check.IsNil) + + regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName) + matched, _ := regexp.MatchString(regex, out) + c.Assert(matched, check.Equals, true, + check.Commentf("iptables output should have contained %q, but was %q", regex, out)) + + // Pinging another container must fail with --icc=false + pingContainers(c, d, true) + + ipStr := "192.171.1.1/24" + ip, _, _ := net.ParseCIDR(ipStr) + ifName := "icc-dummy" + + createInterface(c, "dummy", ifName, ipStr) + + // But, Pinging external or a Host interface must succeed + pingCmd := fmt.Sprintf("ping -c 1 %s -W 1", ip.String()) + runArgs := []string{"--rm", "busybox", "sh", "-c", pingCmd} + _, err = d.Cmd("run", runArgs...) + c.Assert(err, check.IsNil) + + // Reset to Defaults + deleteInterface(c, ifName) + d.Restart() +} + +func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) { + d := s.d + + bridgeName := "external-bridge" + bridgeIp := "192.169.1.1/24" + + createInterface(c, "bridge", bridgeName, bridgeIp) + + args := []string{"--bridge", bridgeName, "--icc=false"} + err := d.StartWithBusybox(args...) + c.Assert(err, check.IsNil) + + ipTablesCmd := exec.Command("iptables", "-nvL", "FORWARD") + out, _, err := runCommandWithOutput(ipTablesCmd) + c.Assert(err, check.IsNil) + + regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName) + matched, _ := regexp.MatchString(regex, out) + c.Assert(matched, check.Equals, true, + check.Commentf("iptables output should have contained %q, but was %q", regex, out)) + + _, err = d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567") + c.Assert(err, check.IsNil) + + out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567") + c.Assert(err, check.IsNil, check.Commentf(out)) + + // Reset to Defaults + deleteInterface(c, bridgeName) + d.Restart() +} + func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { testRequires(c, NativeExecDriver) @@ -1074,15 +1143,27 @@ func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) { } } -func pingContainers(c *check.C) { - runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "container1", - "--hostname", "fred", "busybox", "top") - _, err := runCommand(runCmd) +func pingContainers(c *check.C, d *Daemon, expectFailure bool) { + var dargs []string + if d != nil { + dargs = []string{"--host", d.sock()} + } + + args := append(dargs, "run", "-d", "--name", "container1", "busybox", "top") + _, err := runCommand(exec.Command(dockerBinary, args...)) c.Assert(err, check.IsNil) - runArgs := []string{"run", "--rm", "--link", "container1:alias1", "busybox", "sh", "-c"} + args = append(dargs, "run", "--rm", "--link", "container1:alias1", "busybox", "sh", "-c") pingCmd := "ping -c 1 %s -W 1" + args = append(args, fmt.Sprintf(pingCmd, "alias1")) + _, err = runCommand(exec.Command(dockerBinary, args...)) + + if expectFailure { + c.Assert(err, check.NotNil) + } else { + c.Assert(err, check.IsNil) + } - dockerCmd(c, append(runArgs, fmt.Sprintf(pingCmd, "alias1"))...) - dockerCmd(c, "rm", "-f", "container1") + args = append(dargs, "rm", "-f", "container1") + runCommand(exec.Command(dockerBinary, args...)) } diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index a29b6c5928bda..a1a845baff67d 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -581,8 +581,8 @@ func findContainerIP(c *check.C, id string, vargs ...string) string { return strings.Trim(out, " \r\n'") } -func (d *Daemon) findContainerIP(c *check.C, id string) string { - return findContainerIP(c, id, "--host", d.sock()) +func (d *Daemon) findContainerIP(id string) string { + return findContainerIP(d.c, id, "--host", d.sock()) } func getContainerCount() (int, error) { From 1c073ec1766e0f3cfe28d8f9c2e9a9a37154ece6 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Wed, 29 Apr 2015 11:41:13 -0700 Subject: [PATCH 033/321] Moved explicit cleanups into defered action Signed-off-by: Madhu Venugopal --- integration-cli/docker_cli_daemon_test.go | 77 +++++++++++------------ 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 738d9b0aa2474..17141ddef0806 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -454,19 +454,22 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { d := s.d err := d.Start("--bridge", "nosuchbridge") c.Assert(err, check.NotNil, check.Commentf("--bridge option with an invalid bridge should cause the daemon to fail")) + defer d.Restart() bridgeName := "external-bridge" bridgeIp := "192.169.1.1/24" _, bridgeIPNet, _ := net.ParseCIDR(bridgeIp) - createInterface(c, "bridge", bridgeName, bridgeIp) + out, err := createInterface(c, "bridge", bridgeName, bridgeIp) + c.Assert(err, check.IsNil, check.Commentf(out)) + defer deleteInterface(c, bridgeName) err = d.StartWithBusybox("--bridge", bridgeName) c.Assert(err, check.IsNil) ipTablesSearchString := bridgeIPNet.String() ipTablesCmd := exec.Command("iptables", "-t", "nat", "-nvL") - out, _, err := runCommandWithOutput(ipTablesCmd) + out, _, err = runCommandWithOutput(ipTablesCmd) c.Assert(err, check.IsNil) c.Assert(strings.Contains(out, ipTablesSearchString), check.Equals, true, @@ -481,25 +484,23 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) { c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, check.Commentf("Container IP-Address must be in the same subnet range : %s", containerIp)) - - // Reset to Defaults - deleteInterface(c, bridgeName) - d.Restart() } -func createInterface(c *check.C, ifType string, ifName string, ipNet string) { +func createInterface(c *check.C, ifType string, ifName string, ipNet string) (string, error) { args := []string{"link", "add", "name", ifName, "type", ifType} ipLinkCmd := exec.Command("ip", args...) out, _, err := runCommandWithOutput(ipLinkCmd) - c.Assert(err, check.IsNil, check.Commentf(out)) + if err != nil { + return out, err + } ifCfgCmd := exec.Command("ifconfig", ifName, ipNet, "up") out, _, err = runCommandWithOutput(ifCfgCmd) - c.Assert(err, check.IsNil, check.Commentf(out)) + return out, err } -func deleteInterface(c *check.C, bridge string) { - ifCmd := exec.Command("ip", "link", "delete", bridge) +func deleteInterface(c *check.C, ifName string) { + ifCmd := exec.Command("ip", "link", "delete", ifName) out, _, err := runCommandWithOutput(ifCmd) c.Assert(err, check.IsNil, check.Commentf(out)) @@ -519,9 +520,8 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { // 3. Check if the bip config has taken effect using ifconfig and iptables commands // 4. Launch a Container and make sure the IP-Address is in the expected subnet // 5. Delete the docker0 Bridge - // 6. Restart the Docker Daemon (with no --bip settings) + // 6. Restart the Docker Daemon (via defered action) // This Restart takes care of bringing docker0 interface back to auto-assigned IP - // 7. Stop the Docker Daemon (via defered action) defaultNetworkBridge := "docker0" deleteInterface(c, defaultNetworkBridge) @@ -533,6 +533,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { err := d.StartWithBusybox("--bip", bridgeIp) c.Assert(err, check.IsNil) + defer d.Restart() ifconfigSearchString := ip.String() ifconfigCmd := exec.Command("ifconfig", defaultNetworkBridge) @@ -560,11 +561,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) { c.Assert(bridgeIPNet.Contains(ip), check.Equals, true, check.Commentf("Container IP-Address must be in the same subnet range : %s", containerIp)) - - // Reset to Defaults deleteInterface(c, defaultNetworkBridge) - d.Restart() - pingContainers(c, nil, false) } func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { @@ -573,11 +570,14 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { bridgeName := "external-bridge" bridgeIp := "192.169.1.1/24" - createInterface(c, "bridge", bridgeName, bridgeIp) + out, err := createInterface(c, "bridge", bridgeName, bridgeIp) + c.Assert(err, check.IsNil, check.Commentf(out)) + defer deleteInterface(c, bridgeName) args := []string{"--bridge", bridgeName, "--fixed-cidr", "192.169.1.0/30"} - err := d.StartWithBusybox(args...) + err = d.StartWithBusybox(args...) c.Assert(err, check.IsNil) + defer d.Restart() for i := 0; i < 4; i++ { cName := "Container" + strconv.Itoa(i) @@ -587,10 +587,6 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { check.Commentf("Could not run a Container : %s %s", err.Error(), out)) } } - - // Reset to Defaults - deleteInterface(c, bridgeName) - d.Restart() } func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { @@ -601,6 +597,7 @@ func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { args := []string{"--ip", ip.String()} err := d.StartWithBusybox(args...) c.Assert(err, check.IsNil) + defer d.Restart() out, err := d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") c.Assert(err, check.NotNil, @@ -608,7 +605,9 @@ func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { c.Assert(strings.Contains(out, "Error starting userland proxy"), check.Equals, true) ifName := "dummy" - createInterface(c, "dummy", ifName, ipStr) + out, err = createInterface(c, "dummy", ifName, ipStr) + c.Assert(err, check.IsNil, check.Commentf(out)) + defer deleteInterface(c, ifName) _, err = d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top") c.Assert(err, check.IsNil) @@ -621,10 +620,6 @@ func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) { matched, _ := regexp.MatchString(regex, out) c.Assert(matched, check.Equals, true, check.Commentf("iptables output should have contained %q, but was %q", regex, out)) - - // Reset to Defaults - deleteInterface(c, ifName) - d.Restart() } func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) { @@ -633,14 +628,17 @@ func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) { bridgeName := "external-bridge" bridgeIp := "192.169.1.1/24" - createInterface(c, "bridge", bridgeName, bridgeIp) + out, err := createInterface(c, "bridge", bridgeName, bridgeIp) + c.Assert(err, check.IsNil, check.Commentf(out)) + defer deleteInterface(c, bridgeName) args := []string{"--bridge", bridgeName, "--icc=false"} - err := d.StartWithBusybox(args...) + err = d.StartWithBusybox(args...) c.Assert(err, check.IsNil) + defer d.Restart() ipTablesCmd := exec.Command("iptables", "-nvL", "FORWARD") - out, _, err := runCommandWithOutput(ipTablesCmd) + out, _, err = runCommandWithOutput(ipTablesCmd) c.Assert(err, check.IsNil) regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName) @@ -662,10 +660,6 @@ func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) { runArgs := []string{"--rm", "busybox", "sh", "-c", pingCmd} _, err = d.Cmd("run", runArgs...) c.Assert(err, check.IsNil) - - // Reset to Defaults - deleteInterface(c, ifName) - d.Restart() } func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) { @@ -674,14 +668,17 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) { bridgeName := "external-bridge" bridgeIp := "192.169.1.1/24" - createInterface(c, "bridge", bridgeName, bridgeIp) + out, err := createInterface(c, "bridge", bridgeName, bridgeIp) + c.Assert(err, check.IsNil, check.Commentf(out)) + defer deleteInterface(c, bridgeName) args := []string{"--bridge", bridgeName, "--icc=false"} - err := d.StartWithBusybox(args...) + err = d.StartWithBusybox(args...) c.Assert(err, check.IsNil) + defer d.Restart() ipTablesCmd := exec.Command("iptables", "-nvL", "FORWARD") - out, _, err := runCommandWithOutput(ipTablesCmd) + out, _, err = runCommandWithOutput(ipTablesCmd) c.Assert(err, check.IsNil) regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName) @@ -694,10 +691,6 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) { out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567") c.Assert(err, check.IsNil, check.Commentf(out)) - - // Reset to Defaults - deleteInterface(c, bridgeName) - d.Restart() } func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) { From c271c61feea7d3ea3fcbb3af9f0d9c1f641a8d82 Mon Sep 17 00:00:00 2001 From: Aaron Davidson Date: Sun, 12 Apr 2015 16:56:05 -0700 Subject: [PATCH 034/321] Do our best not to invoke iptables concurrently if --wait is unsupported We encountered a situation where concurrent invocations of the docker daemon on a machine with an older version of iptables led to nondeterministic errors related to simultaenous invocations of iptables. While this is best resolved by upgrading iptables itself, the particular situation would have been avoided if the docker daemon simply took care not to concurrently invoke iptables. Of course, external processes could also cause iptables to fail in this way, but invoking docker in parallel seems like a pretty common case. Signed-off-by: Aaron Davidson --- pkg/iptables/iptables.go | 10 +++++++-- pkg/iptables/iptables_test.go | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/pkg/iptables/iptables.go b/pkg/iptables/iptables.go index 0cfcca7502231..fcedd0f341d31 100644 --- a/pkg/iptables/iptables.go +++ b/pkg/iptables/iptables.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "strings" + "sync" "github.com/Sirupsen/logrus" ) @@ -25,8 +26,10 @@ const ( ) var ( - iptablesPath string - supportsXlock = false + iptablesPath string + supportsXlock = false + // used to lock iptables commands if xtables lock is not supported + bestEffortLock sync.Mutex ErrIptablesNotFound = errors.New("Iptables not found") ) @@ -288,6 +291,9 @@ func Raw(args ...string) ([]byte, error) { } if supportsXlock { args = append([]string{"--wait"}, args...) + } else { + bestEffortLock.Lock() + defer bestEffortLock.Unlock() } logrus.Debugf("%s, %v", iptablesPath, args) diff --git a/pkg/iptables/iptables_test.go b/pkg/iptables/iptables_test.go index ced4262ce2941..840aca14b9614 100644 --- a/pkg/iptables/iptables_test.go +++ b/pkg/iptables/iptables_test.go @@ -5,6 +5,7 @@ import ( "os/exec" "strconv" "strings" + "sync" "testing" ) @@ -169,6 +170,45 @@ func TestOutput(t *testing.T) { } } +func TestConcurrencyWithWait(t *testing.T) { + RunConcurrencyTest(t, true) +} + +func TestConcurrencyNoWait(t *testing.T) { + RunConcurrencyTest(t, false) +} + +// Runs 10 concurrent rule additions. This will fail if iptables +// is actually invoked simultaneously without --wait. +// Note that if iptables does not support the xtable lock on this +// system, then allowXlock has no effect -- it will always be off. +func RunConcurrencyTest(t *testing.T, allowXlock bool) { + var wg sync.WaitGroup + + if !allowXlock && supportsXlock { + supportsXlock = false + defer func() { supportsXlock = true }() + } + + ip := net.ParseIP("192.168.1.1") + port := 1234 + dstAddr := "172.17.0.1" + dstPort := 4321 + proto := "tcp" + + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + err := natChain.Forward(Append, ip, port, proto, dstAddr, dstPort) + if err != nil { + t.Fatal(err) + } + }() + } + wg.Wait() +} + func TestCleanup(t *testing.T) { var err error var rules []byte From c0a1b2d6e99d5c6e29a016da39c1313a54c1cb34 Mon Sep 17 00:00:00 2001 From: Ahmet Alp Balkan Date: Mon, 20 Apr 2015 16:15:27 -0700 Subject: [PATCH 035/321] docs: Add more places docker.service can be at More systemd goodness. Documenting where `docker.service` lives under Ubuntu 15. Signed-off-by: Ahmet Alp Balkan --- docs/sources/articles/systemd.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/articles/systemd.md b/docs/sources/articles/systemd.md index fddd146b0702b..10baf6d6f62ea 100644 --- a/docs/sources/articles/systemd.md +++ b/docs/sources/articles/systemd.md @@ -30,8 +30,8 @@ If the `docker.service` file is set to use an `EnvironmentFile` (often pointing to `/etc/sysconfig/docker`) then you can modify the referenced file. -Or, you may need to edit the `docker.service` file, which can be in `/usr/lib/systemd/system` -or `/etc/systemd/service`. +Or, you may need to edit the `docker.service` file, which can be in +`/usr/lib/systemd/system`, `/etc/systemd/service`, or `/lib/systemd/system`. ### Runtime directory and storage driver From 531f4122bdcd4de289f613a5ef010f4c1989f098 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 27 Apr 2015 23:11:29 +0200 Subject: [PATCH 036/321] Remove engine mechanism Signed-off-by: Antonio Murdaca --- api/client/start.go | 13 +- api/client/utils.go | 15 +- api/client/version.go | 29 ++-- api/server/server.go | 171 ++++++++----------- daemon/container.go | 14 +- daemon/daemon.go | 185 ++++++++------------ docker/daemon.go | 96 ++++++++--- engine/engine.go | 255 ---------------------------- engine/engine_test.go | 236 -------------------------- engine/env.go | 313 ---------------------------------- engine/env_test.go | 366 ---------------------------------------- engine/hack.go | 21 --- engine/helpers_test.go | 11 -- engine/http.go | 42 ----- engine/job.go | 222 ------------------------ engine/job_test.go | 47 ------ engine/shutdown_test.go | 78 --------- engine/streams.go | 188 --------------------- engine/streams_test.go | 215 ----------------------- pkg/pidfile/pidfile.go | 10 +- 20 files changed, 240 insertions(+), 2287 deletions(-) delete mode 100644 engine/engine.go delete mode 100644 engine/engine_test.go delete mode 100644 engine/env.go delete mode 100644 engine/env_test.go delete mode 100644 engine/hack.go delete mode 100644 engine/helpers_test.go delete mode 100644 engine/http.go delete mode 100644 engine/job.go delete mode 100644 engine/job_test.go delete mode 100644 engine/shutdown_test.go delete mode 100644 engine/streams.go delete mode 100644 engine/streams_test.go diff --git a/api/client/start.go b/api/client/start.go index d3dec9489d14a..b290524cafc7f 100644 --- a/api/client/start.go +++ b/api/client/start.go @@ -1,13 +1,14 @@ package client import ( + "encoding/json" "fmt" "io" "net/url" "os" "github.com/Sirupsen/logrus" - "github.com/docker/docker/engine" + "github.com/docker/docker/api/types" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/signal" @@ -65,12 +66,12 @@ func (cli *DockerCli) CmdStart(args ...string) error { return err } - env := engine.Env{} - if err := env.Decode(stream); err != nil { + var c types.ContainerJSON + if err := json.NewDecoder(stream).Decode(&c); err != nil { return err } - config := env.GetSubEnv("Config") - tty = config.GetBool("Tty") + + tty = c.Config.Tty if !tty { sigc := cli.forwardAllSignals(cmd.Arg(0)) @@ -82,7 +83,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { v := url.Values{} v.Set("stream", "1") - if *openStdin && config.GetBool("OpenStdin") { + if *openStdin && c.Config.OpenStdin { v.Set("stdin", "1") in = cli.in } diff --git a/api/client/utils.go b/api/client/utils.go index 7a52ad25f47aa..eed1163f838d8 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -22,7 +22,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/cliconfig" - "github.com/docker/docker/engine" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/stdcopy" @@ -42,18 +41,8 @@ func (cli *DockerCli) HTTPClient() *http.Client { func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) { params := bytes.NewBuffer(nil) if data != nil { - if env, ok := data.(engine.Env); ok { - if err := env.Encode(params); err != nil { - return nil, err - } - } else { - buf, err := json.Marshal(data) - if err != nil { - return nil, err - } - if _, err := params.Write(buf); err != nil { - return nil, err - } + if err := json.NewEncoder(params).Encode(data); err != nil { + return nil, err } } return params, nil diff --git a/api/client/version.go b/api/client/version.go index 25a7e367e2edf..2fb6f8a8d54c8 100644 --- a/api/client/version.go +++ b/api/client/version.go @@ -1,13 +1,14 @@ package client import ( + "encoding/json" "fmt" "runtime" "github.com/Sirupsen/logrus" "github.com/docker/docker/api" + "github.com/docker/docker/api/types" "github.com/docker/docker/autogen/dockerversion" - "github.com/docker/docker/engine" flag "github.com/docker/docker/pkg/mflag" ) @@ -32,28 +33,24 @@ func (cli *DockerCli) CmdVersion(args ...string) error { } fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH) - body, _, err := readBody(cli.call("GET", "/version", nil, nil)) + stream, _, err := cli.call("GET", "/version", nil, nil) if err != nil { return err } - out := engine.NewOutput() - remoteVersion, err := out.AddEnv() - if err != nil { - logrus.Errorf("Error reading remote version: %s", err) - return err - } - if _, err := out.Write(body); err != nil { + var v types.Version + if err := json.NewDecoder(stream).Decode(&v); err != nil { logrus.Errorf("Error reading remote version: %s", err) return err } - out.Close() - fmt.Fprintf(cli.out, "Server version: %s\n", remoteVersion.Get("Version")) - if apiVersion := remoteVersion.Get("ApiVersion"); apiVersion != "" { - fmt.Fprintf(cli.out, "Server API version: %s\n", apiVersion) + + fmt.Fprintf(cli.out, "Server version: %s\n", v.Version) + if v.ApiVersion != "" { + fmt.Fprintf(cli.out, "Server API version: %s\n", v.ApiVersion) } - fmt.Fprintf(cli.out, "Go version (server): %s\n", remoteVersion.Get("GoVersion")) - fmt.Fprintf(cli.out, "Git commit (server): %s\n", remoteVersion.Get("GitCommit")) - fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", remoteVersion.Get("Os"), remoteVersion.Get("Arch")) + fmt.Fprintf(cli.out, "Go version (server): %s\n", v.GoVersion) + fmt.Fprintf(cli.out, "Git commit (server): %s\n", v.GitCommit) + fmt.Fprintf(cli.out, "OS/Arch (server): %s/%s\n", v.Os, v.Arch) + return nil } diff --git a/api/server/server.go b/api/server/server.go index 61e8162659562..3a7975fe69e24 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -1,9 +1,6 @@ package server import ( - "runtime" - "time" - "encoding/base64" "encoding/json" "fmt" @@ -11,8 +8,10 @@ import ( "net" "net/http" "os" + "runtime" "strconv" "strings" + "time" "code.google.com/p/go.net/websocket" "github.com/gorilla/mux" @@ -25,7 +24,6 @@ import ( "github.com/docker/docker/cliconfig" "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/networkdriver/bridge" - "github.com/docker/docker/engine" "github.com/docker/docker/graph" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/parsers" @@ -53,26 +51,31 @@ type ServerConfig struct { } type Server struct { - daemon *daemon.Daemon - cfg *ServerConfig - router *mux.Router - start chan struct{} - - // TODO: delete engine - eng *engine.Engine + daemon *daemon.Daemon + cfg *ServerConfig + router *mux.Router + start chan struct{} + servers []serverCloser } -func New(cfg *ServerConfig, eng *engine.Engine) *Server { +func New(cfg *ServerConfig) *Server { srv := &Server{ cfg: cfg, start: make(chan struct{}), - eng: eng, } - r := createRouter(srv, eng) + r := createRouter(srv) srv.router = r return srv } +func (s *Server) Close() { + for _, srv := range s.servers { + if err := srv.Close(); err != nil { + logrus.Error(err) + } + } +} + func (s *Server) SetDaemon(d *daemon.Daemon) { s.daemon = d } @@ -92,19 +95,15 @@ func (s *Server) ServeApi(protoAddrs []string) error { if len(protoAddrParts) != 2 { return fmt.Errorf("bad format, expected PROTO://ADDR") } + srv, err := s.newServer(protoAddrParts[0], protoAddrParts[1]) + if err != nil { + return err + } + s.servers = append(s.servers, srv) + go func(proto, addr string) { logrus.Infof("Listening for HTTP on %s (%s)", proto, addr) - srv, err := s.newServer(proto, addr) - if err != nil { - chErrors <- err - return - } - s.eng.OnShutdown(func() { - if err := srv.Close(); err != nil { - logrus.Error(err) - } - }) - if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") { + if err := srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") { err = nil } chErrors <- err @@ -133,7 +132,7 @@ func (s *HttpServer) Close() error { return s.l.Close() } -type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error +type HttpApiFunc func(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { conn, _, err := w.(http.Hijacker).Hijack() @@ -230,16 +229,7 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error { return json.NewEncoder(w).Encode(v) } -func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) { - w.Header().Set("Content-Type", "application/json") - if flush { - out.Add(utils.NewWriteFlusher(w)) - } else { - out.Add(w) - } -} - -func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postAuth(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var config *cliconfig.AuthConfig err := json.NewDecoder(r.Body).Decode(&config) r.Body.Close() @@ -255,7 +245,7 @@ func (s *Server) postAuth(eng *engine.Engine, version version.Version, w http.Re }) } -func (s *Server) getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getVersion(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.Header().Set("Content-Type", "application/json") v := &types.Version{ @@ -273,7 +263,7 @@ func (s *Server) getVersion(eng *engine.Engine, version version.Version, w http. return writeJSON(w, http.StatusOK, v) } -func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersKill(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -308,7 +298,7 @@ func (s *Server) postContainersKill(eng *engine.Engine, version version.Version, return nil } -func (s *Server) postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersPause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -332,7 +322,7 @@ func (s *Server) postContainersPause(eng *engine.Engine, version version.Version return nil } -func (s *Server) postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersUnpause(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -356,7 +346,7 @@ func (s *Server) postContainersUnpause(eng *engine.Engine, version version.Versi return nil } -func (s *Server) getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersExport(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -364,7 +354,7 @@ func (s *Server) getContainersExport(eng *engine.Engine, version version.Version return s.daemon.ContainerExport(vars["name"], w) } -func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -405,7 +395,7 @@ func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w ht return writeJSON(w, http.StatusOK, legacyImages) } -func (s *Server) getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getInfo(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.Header().Set("Content-Type", "application/json") info, err := s.daemon.SystemInfo() @@ -416,7 +406,7 @@ func (s *Server) getInfo(eng *engine.Engine, version version.Version, w http.Res return writeJSON(w, http.StatusOK, info) } -func (s *Server) getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getEvents(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -520,7 +510,7 @@ func (s *Server) getEvents(eng *engine.Engine, version version.Version, w http.R } } -func (s *Server) getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -534,7 +524,7 @@ func (s *Server) getImagesHistory(eng *engine.Engine, version version.Version, w return writeJSON(w, http.StatusOK, history) } -func (s *Server) getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersChanges(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -553,7 +543,7 @@ func (s *Server) getContainersChanges(eng *engine.Engine, version version.Versio return writeJSON(w, http.StatusOK, changes) } -func (s *Server) getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersTop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version.LessThan("1.4") { return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.") } @@ -574,7 +564,7 @@ func (s *Server) getContainersTop(eng *engine.Engine, version version.Version, w return writeJSON(w, http.StatusOK, procList) } -func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -603,7 +593,7 @@ func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version, return writeJSON(w, http.StatusOK, containers) } -func (s *Server) getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersStats(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -614,7 +604,7 @@ func (s *Server) getContainersStats(eng *engine.Engine, version version.Version, return s.daemon.ContainerStats(vars["name"], utils.NewWriteFlusher(w)) } -func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersLogs(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -644,7 +634,7 @@ func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, return nil } -func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -662,7 +652,7 @@ func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w ht return nil } -func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -708,7 +698,7 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http. } // Creates an image from Pull or from Import -func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -788,7 +778,7 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w return nil } -func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -818,7 +808,7 @@ func (s *Server) getImagesSearch(eng *engine.Engine, version version.Version, w return json.NewEncoder(w).Encode(query.Results) } -func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -875,7 +865,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h } -func (s *Server) getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -907,11 +897,11 @@ func (s *Server) getImagesGet(eng *engine.Engine, version version.Version, w htt } -func (s *Server) postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { return s.daemon.Repositories().Load(r.Body, w) } -func (s *Server) postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return nil } @@ -939,7 +929,7 @@ func (s *Server) postContainersCreate(eng *engine.Engine, version version.Versio }) } -func (s *Server) postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersRestart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -961,7 +951,7 @@ func (s *Server) postContainersRestart(eng *engine.Engine, version version.Versi return nil } -func (s *Server) postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerRename(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -978,7 +968,7 @@ func (s *Server) postContainerRename(eng *engine.Engine, version version.Version return nil } -func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) deleteContainers(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1006,7 +996,7 @@ func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w return nil } -func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1026,7 +1016,7 @@ func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w htt return writeJSON(w, http.StatusOK, list) } -func (s *Server) postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -1062,7 +1052,7 @@ func (s *Server) postContainersStart(eng *engine.Engine, version version.Version return nil } -func (s *Server) postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersStop(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1087,7 +1077,7 @@ func (s *Server) postContainersStop(eng *engine.Engine, version version.Version, return nil } -func (s *Server) postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersWait(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -1105,7 +1095,7 @@ func (s *Server) postContainersWait(eng *engine.Engine, version version.Version, }) } -func (s *Server) postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1130,7 +1120,7 @@ func (s *Server) postContainersResize(eng *engine.Engine, version version.Versio return cont.Resize(height, width) } -func (s *Server) postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1185,7 +1175,7 @@ func (s *Server) postContainersAttach(eng *engine.Engine, version version.Versio return nil } -func (s *Server) wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) wsContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1211,7 +1201,7 @@ func (s *Server) wsContainersAttach(eng *engine.Engine, version version.Version, return nil } -func (s *Server) getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getContainersByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -1232,7 +1222,7 @@ func (s *Server) getContainersByName(eng *engine.Engine, version version.Version return writeJSON(w, http.StatusOK, containerJSON) } -func (s *Server) getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getExecByID(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter 'id'") } @@ -1245,7 +1235,7 @@ func (s *Server) getExecByID(eng *engine.Engine, version version.Version, w http return writeJSON(w, http.StatusOK, eConfig) } -func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -1268,7 +1258,7 @@ func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w return writeJSON(w, http.StatusOK, imageInspect) } -func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version.LessThan("1.3") { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } @@ -1363,7 +1353,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R return nil } -func (s *Server) postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainersCopy(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } @@ -1413,7 +1403,7 @@ func (s *Server) postContainersCopy(eng *engine.Engine, version version.Version, return nil } -func (s *Server) postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerExecCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return nil } @@ -1442,7 +1432,7 @@ func (s *Server) postContainerExecCreate(eng *engine.Engine, version version.Ver } // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start. -func (s *Server) postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerExecStart(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return nil } @@ -1495,7 +1485,7 @@ func (s *Server) postContainerExecStart(eng *engine.Engine, version version.Vers return nil } -func (s *Server) postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) postContainerExecResize(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err } @@ -1515,7 +1505,7 @@ func (s *Server) postContainerExecResize(eng *engine.Engine, version version.Ver return s.daemon.ContainerExecResize(vars["name"], height, width) } -func (s *Server) optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) optionsHandler(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { w.WriteHeader(http.StatusOK) return nil } @@ -1526,12 +1516,12 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") } -func (s *Server) ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func (s *Server) ping(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { _, err := w.Write([]byte{'O', 'K'}) return err } -func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc { +func makeHttpHandler(logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // log the request logrus.Debugf("Calling %s %s", localMethod, localRoute) @@ -1559,7 +1549,7 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local return } - if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil { + if err := handlerFunc(version, w, r, mux.Vars(r)); err != nil { logrus.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) httpError(w, err) } @@ -1567,7 +1557,7 @@ func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, local } // we keep enableCors just for legacy usage, need to be removed in the future -func createRouter(s *Server, eng *engine.Engine) *mux.Router { +func createRouter(s *Server) *mux.Router { r := mux.NewRouter() if os.Getenv("DEBUG") != "" { ProfilerSetup(r, "/debug/") @@ -1644,7 +1634,7 @@ func createRouter(s *Server, eng *engine.Engine) *mux.Router { localMethod := method // build the handler function - f := makeHttpHandler(eng, s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version)) + f := makeHttpHandler(s.cfg.Logging, localMethod, localRoute, localFct, corsHeaders, version.Version(s.cfg.Version)) // add the new route if localRoute == "" { @@ -1659,23 +1649,6 @@ func createRouter(s *Server, eng *engine.Engine) *mux.Router { return r } -// ServeRequest processes a single http request to the docker remote api. -// FIXME: refactor this to be part of Server and not require re-creating a new -// router each time. This requires first moving ListenAndServe into Server. -func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) { - cfg := &ServerConfig{ - EnableCors: true, - Version: string(apiversion), - } - api := New(cfg, eng) - daemon, _ := eng.HackGetGlobalVar("httpapi.daemon").(*daemon.Daemon) - api.AcceptConnections(daemon) - router := createRouter(api, eng) - // Insert APIVERSION into the request as a convenience - req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path) - router.ServeHTTP(w, req) -} - func allocateDaemonPort(addr string) error { host, port, err := net.SplitHostPort(addr) if err != nil { diff --git a/daemon/container.go b/daemon/container.go index 9bd8cc1eb90cd..ef42295344c44 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -26,7 +26,6 @@ import ( "github.com/docker/docker/daemon/logger/syslog" "github.com/docker/docker/daemon/network" "github.com/docker/docker/daemon/networkdriver/bridge" - "github.com/docker/docker/engine" "github.com/docker/docker/image" "github.com/docker/docker/links" "github.com/docker/docker/nat" @@ -600,10 +599,7 @@ func (container *Container) AllocateNetwork() error { return nil } - var ( - err error - eng = container.daemon.eng - ) + var err error networkSettings, err := bridge.Allocate(container.ID, container.Config.MacAddress, "", "") if err != nil { @@ -650,7 +646,7 @@ func (container *Container) AllocateNetwork() error { container.NetworkSettings.PortMapping = nil for port := range portSpecs { - if err = container.allocatePort(eng, port, bindings); err != nil { + if err = container.allocatePort(port, bindings); err != nil { bridge.Release(container.ID) return err } @@ -686,8 +682,6 @@ func (container *Container) RestoreNetwork() error { return nil } - eng := container.daemon.eng - // Re-allocate the interface with the same IP and MAC address. if _, err := bridge.Allocate(container.ID, container.NetworkSettings.MacAddress, container.NetworkSettings.IPAddress, ""); err != nil { return err @@ -695,7 +689,7 @@ func (container *Container) RestoreNetwork() error { // Re-allocate any previously allocated ports. for port := range container.NetworkSettings.Ports { - if err := container.allocatePort(eng, port, container.NetworkSettings.Ports); err != nil { + if err := container.allocatePort(port, container.NetworkSettings.Ports); err != nil { return err } } @@ -1483,7 +1477,7 @@ func (container *Container) waitForStart() error { return nil } -func (container *Container) allocatePort(eng *engine.Engine, port nat.Port, bindings nat.PortMap) error { +func (container *Container) allocatePort(port nat.Port, bindings nat.PortMap) error { binding := bindings[port] if container.hostConfig.PublishAllPorts && len(binding) == 0 { binding = append(binding, nat.PortBinding{}) diff --git a/daemon/daemon.go b/daemon/daemon.go index 05de402174c82..130fcc46f245c 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -27,7 +27,6 @@ import ( _ "github.com/docker/docker/daemon/graphdriver/vfs" "github.com/docker/docker/daemon/network" "github.com/docker/docker/daemon/networkdriver/bridge" - "github.com/docker/docker/engine" "github.com/docker/docker/graph" "github.com/docker/docker/image" "github.com/docker/docker/pkg/archive" @@ -38,7 +37,6 @@ import ( "github.com/docker/docker/pkg/namesgenerator" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/docker/pkg/pidfile" "github.com/docker/docker/pkg/resolvconf" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/sysinfo" @@ -103,7 +101,6 @@ type Daemon struct { idIndex *truncindex.TruncIndex sysInfo *sysinfo.SysInfo volumes *volumes.Repository - eng *engine.Engine config *Config containerGraph *graphdb.Database driver graphdriver.Driver @@ -114,14 +111,6 @@ type Daemon struct { EventsService *events.Events } -// Install installs daemon capabilities to eng. -func (daemon *Daemon) Install(eng *engine.Engine) error { - // FIXME: this hack is necessary for legacy integration tests to access - // the daemon object. - eng.HackSetGlobalVar("httpapi.daemon", daemon) - return nil -} - // Get looks for a container using the provided information, which could be // one of the following inputs from the caller: // - A full container ID, which will exact match a container in daemon's list @@ -741,16 +730,7 @@ func (daemon *Daemon) RegisterLinks(container *Container, hostConfig *runconfig. return nil } -// FIXME: harmonize with NewGraph() -func NewDaemon(config *Config, eng *engine.Engine, registryService *registry.Service) (*Daemon, error) { - daemon, err := NewDaemonFromDirectory(config, eng, registryService) - if err != nil { - return nil, err - } - return daemon, nil -} - -func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService *registry.Service) (*Daemon, error) { +func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemon, err error) { if config.Mtu == 0 { config.Mtu = getDefaultNetworkMtu() } @@ -766,19 +746,6 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService } config.DisableNetwork = config.Bridge.Iface == disableNetworkBridge - // Claim the pidfile first, to avoid any and all unexpected race conditions. - // Some of the init doesn't need a pidfile lock - but let's not try to be smart. - if config.Pidfile != "" { - file, err := pidfile.New(config.Pidfile) - if err != nil { - return nil, err - } - eng.OnShutdown(func() { - // Always release the pidfile last, just in case - file.Remove() - }) - } - // Check that the system is supported and we have sufficient privileges if runtime.GOOS != "linux" { return nil, fmt.Errorf("The Docker daemon is only supported on linux") @@ -826,17 +793,22 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService return nil, fmt.Errorf("error initializing graphdriver: %v", err) } logrus.Debugf("Using graph driver %s", driver) - // register cleanup for graph driver - eng.OnShutdown(func() { - if err := driver.Cleanup(); err != nil { - logrus.Errorf("Error during graph storage driver.Cleanup(): %v", err) + + d := &Daemon{} + d.driver = driver + + defer func() { + if err != nil { + if err := d.Shutdown(); err != nil { + logrus.Error(err) + } } - }) + }() if config.EnableSelinuxSupport { if selinuxEnabled() { // As Docker on btrfs and SELinux are incompatible at present, error on both being enabled - if driver.String() == "btrfs" { + if d.driver.String() == "btrfs" { return nil, fmt.Errorf("SELinux is not supported with the BTRFS graph driver") } logrus.Debug("SELinux enabled successfully") @@ -854,12 +826,12 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService } // Migrate the container if it is aufs and aufs is enabled - if err = migrateIfAufs(driver, config.Root); err != nil { + if err := migrateIfAufs(d.driver, config.Root); err != nil { return nil, err } logrus.Debug("Creating images graph") - g, err := graph.NewGraph(path.Join(config.Root, "graph"), driver) + g, err := graph.NewGraph(path.Join(config.Root, "graph"), d.driver) if err != nil { return nil, err } @@ -897,7 +869,7 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService Events: eventsService, Trust: trustService, } - repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), tagCfg) + repositories, err := graph.NewTagStore(path.Join(config.Root, "repositories-"+d.driver.String()), tagCfg) if err != nil { return nil, fmt.Errorf("Couldn't create Tag store: %s", err) } @@ -913,12 +885,8 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService if err != nil { return nil, err } - // register graph close on shutdown - eng.OnShutdown(func() { - if err := graph.Close(); err != nil { - logrus.Errorf("Error during container graph.Close(): %v", err) - } - }) + + d.containerGraph = graph localCopy := path.Join(config.Root, "init", fmt.Sprintf("dockerinit-%s", dockerversion.VERSION)) sysInitPath := utils.DockerInitPath(localCopy) @@ -947,66 +915,67 @@ func NewDaemonFromDirectory(config *Config, eng *engine.Engine, registryService return nil, err } - daemon := &Daemon{ - ID: trustKey.PublicKey().KeyID(), - repository: daemonRepo, - containers: &contStore{s: make(map[string]*Container)}, - execCommands: newExecStore(), - graph: g, - repositories: repositories, - idIndex: truncindex.NewTruncIndex([]string{}), - sysInfo: sysInfo, - volumes: volumes, - config: config, - containerGraph: graph, - driver: driver, - sysInitPath: sysInitPath, - execDriver: ed, - eng: eng, - statsCollector: newStatsCollector(1 * time.Second), - defaultLogConfig: config.LogConfig, - RegistryService: registryService, - EventsService: eventsService, - } - - eng.OnShutdown(func() { - if err := daemon.shutdown(); err != nil { - logrus.Errorf("Error during daemon.shutdown(): %v", err) - } - }) - - if err := daemon.restore(); err != nil { + d.ID = trustKey.PublicKey().KeyID() + d.repository = daemonRepo + d.containers = &contStore{s: make(map[string]*Container)} + d.execCommands = newExecStore() + d.graph = g + d.repositories = repositories + d.idIndex = truncindex.NewTruncIndex([]string{}) + d.sysInfo = sysInfo + d.volumes = volumes + d.config = config + d.sysInitPath = sysInitPath + d.execDriver = ed + d.statsCollector = newStatsCollector(1 * time.Second) + d.defaultLogConfig = config.LogConfig + d.RegistryService = registryService + d.EventsService = eventsService + + if err := d.restore(); err != nil { return nil, err } // set up filesystem watch on resolv.conf for network changes - if err := daemon.setupResolvconfWatcher(); err != nil { + if err := d.setupResolvconfWatcher(); err != nil { return nil, err } - return daemon, nil + return d, nil } -func (daemon *Daemon) shutdown() error { - group := sync.WaitGroup{} - logrus.Debug("starting clean shutdown of all containers...") - for _, container := range daemon.List() { - c := container - if c.IsRunning() { - logrus.Debugf("stopping %s", c.ID) - group.Add(1) - - go func() { - defer group.Done() - if err := c.KillSig(15); err != nil { - logrus.Debugf("kill 15 error for %s - %s", c.ID, err) - } - c.WaitStop(-1 * time.Second) - logrus.Debugf("container stopped %s", c.ID) - }() +func (daemon *Daemon) Shutdown() error { + if daemon.containerGraph != nil { + if err := daemon.containerGraph.Close(); err != nil { + logrus.Errorf("Error during container graph.Close(): %v", err) } } - group.Wait() + if daemon.driver != nil { + if err := daemon.driver.Cleanup(); err != nil { + logrus.Errorf("Error during graph storage driver.Cleanup(): %v", err) + } + } + if daemon.containers != nil { + group := sync.WaitGroup{} + logrus.Debug("starting clean shutdown of all containers...") + for _, container := range daemon.List() { + c := container + if c.IsRunning() { + logrus.Debugf("stopping %s", c.ID) + group.Add(1) + + go func() { + defer group.Done() + if err := c.KillSig(15); err != nil { + logrus.Debugf("kill 15 error for %s - %s", c.ID, err) + } + c.WaitStop(-1 * time.Second) + logrus.Debugf("container stopped %s", c.ID) + }() + } + } + group.Wait() + } return nil } @@ -1087,26 +1056,6 @@ func (daemon *Daemon) UnsubscribeToContainerStats(name string, ch chan interface return nil } -// Nuke kills all containers then removes all content -// from the content root, including images, volumes and -// container filesystems. -// Again: this will remove your entire docker daemon! -// FIXME: this is deprecated, and only used in legacy -// tests. Please remove. -func (daemon *Daemon) Nuke() error { - var wg sync.WaitGroup - for _, container := range daemon.List() { - wg.Add(1) - go func(c *Container) { - c.Kill() - wg.Done() - }(container) - } - wg.Wait() - - return os.RemoveAll(daemon.config.Root) -} - // FIXME: this is a convenience function for integration tests // which need direct access to daemon.graph. // Once the tests switch to using engine and jobs, this method diff --git a/docker/daemon.go b/docker/daemon.go index c6241b60602fb..c78879784a3f0 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "time" "github.com/Sirupsen/logrus" apiserver "github.com/docker/docker/api/server" @@ -14,9 +15,9 @@ import ( "github.com/docker/docker/daemon" _ "github.com/docker/docker/daemon/execdriver/lxc" _ "github.com/docker/docker/daemon/execdriver/native" - "github.com/docker/docker/engine" "github.com/docker/docker/pkg/homedir" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/pidfile" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/timeutils" @@ -83,14 +84,45 @@ func mainDaemon() { logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed}) - eng := engine.New() - signal.Trap(eng.Shutdown) + var pfile *pidfile.PidFile + if daemonCfg.Pidfile != "" { + pf, err := pidfile.New(daemonCfg.Pidfile) + if err != nil { + logrus.Fatalf("Error starting daemon: %v", err) + } + pfile = pf + defer func() { + if err := pfile.Remove(); err != nil { + logrus.Error(err) + } + }() + } if err := migrateKey(); err != nil { logrus.Fatal(err) } daemonCfg.TrustKeyPath = *flTrustKey + registryService := registry.NewService(registryCfg) + d, err := daemon.NewDaemon(daemonCfg, registryService) + if err != nil { + if pfile != nil { + if err := pfile.Remove(); err != nil { + logrus.Error(err) + } + } + logrus.Fatalf("Error starting daemon: %v", err) + } + + logrus.Info("Daemon has completed initialization") + + logrus.WithFields(logrus.Fields{ + "version": dockerversion.VERSION, + "commit": dockerversion.GITCOMMIT, + "execdriver": d.ExecutionDriver().Name(), + "graphdriver": d.GraphDriver().String(), + }).Info("Docker daemon") + serverConfig := &apiserver.ServerConfig{ Logging: true, EnableCors: daemonCfg.EnableCors, @@ -104,7 +136,7 @@ func mainDaemon() { TlsKey: *flKey, } - api := apiserver.New(serverConfig, eng) + api := apiserver.New(serverConfig) // The serve API routine never exits unless an error occurs // We need to start it as a goroutine and wait on it so @@ -119,40 +151,52 @@ func mainDaemon() { serveAPIWait <- nil }() - registryService := registry.NewService(registryCfg) - d, err := daemon.NewDaemon(daemonCfg, eng, registryService) - if err != nil { - eng.Shutdown() - logrus.Fatalf("Error starting daemon: %v", err) - } - - if err := d.Install(eng); err != nil { - eng.Shutdown() - logrus.Fatalf("Error starting daemon: %v", err) - } - - logrus.Info("Daemon has completed initialization") - - logrus.WithFields(logrus.Fields{ - "version": dockerversion.VERSION, - "commit": dockerversion.GITCOMMIT, - "execdriver": d.ExecutionDriver().Name(), - "graphdriver": d.GraphDriver().String(), - }).Info("Docker daemon") + signal.Trap(func() { + api.Close() + <-serveAPIWait + shutdownDaemon(d, 15) + if pfile != nil { + if err := pfile.Remove(); err != nil { + logrus.Error(err) + } + } + }) // after the daemon is done setting up we can tell the api to start // accepting connections with specified daemon api.AcceptConnections(d) // Daemon is fully initialized and handling API traffic - // Wait for serve API job to complete + // Wait for serve API to complete errAPI := <-serveAPIWait - eng.Shutdown() + shutdownDaemon(d, 15) if errAPI != nil { + if pfile != nil { + if err := pfile.Remove(); err != nil { + logrus.Error(err) + } + } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } } +// shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case +// d.Shutdown() is waiting too long to kill container or worst it's +// blocked there +func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { + ch := make(chan struct{}) + go func() { + d.Shutdown() + close(ch) + }() + select { + case <-ch: + logrus.Debug("Clean shutdown succeded") + case <-time.After(timeout * time.Second): + logrus.Error("Force shutdown daemon") + } +} + // currentUserIsOwner checks whether the current user is the owner of the given // file. func currentUserIsOwner(f string) bool { diff --git a/engine/engine.go b/engine/engine.go deleted file mode 100644 index 79fae51cc3848..0000000000000 --- a/engine/engine.go +++ /dev/null @@ -1,255 +0,0 @@ -package engine - -import ( - "bufio" - "fmt" - "io" - "os" - "sort" - "strings" - "sync" - "time" - - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/stringid" -) - -// Installer is a standard interface for objects which can "install" themselves -// on an engine by registering handlers. -// This can be used as an entrypoint for external plugins etc. -type Installer interface { - Install(*Engine) error -} - -type Handler func(*Job) error - -var globalHandlers map[string]Handler - -func init() { - globalHandlers = make(map[string]Handler) -} - -func Register(name string, handler Handler) error { - _, exists := globalHandlers[name] - if exists { - return fmt.Errorf("Can't overwrite global handler for command %s", name) - } - globalHandlers[name] = handler - return nil -} - -func unregister(name string) { - delete(globalHandlers, name) -} - -// The Engine is the core of Docker. -// It acts as a store for *containers*, and allows manipulation of these -// containers by executing *jobs*. -type Engine struct { - handlers map[string]Handler - catchall Handler - hack Hack // data for temporary hackery (see hack.go) - id string - Stdout io.Writer - Stderr io.Writer - Stdin io.Reader - Logging bool - tasks sync.WaitGroup - l sync.RWMutex // lock for shutdown - shutdownWait sync.WaitGroup - shutdown bool - onShutdown []func() // shutdown handlers -} - -func (eng *Engine) Register(name string, handler Handler) error { - _, exists := eng.handlers[name] - if exists { - return fmt.Errorf("Can't overwrite handler for command %s", name) - } - eng.handlers[name] = handler - return nil -} - -func (eng *Engine) RegisterCatchall(catchall Handler) { - eng.catchall = catchall -} - -// New initializes a new engine. -func New() *Engine { - eng := &Engine{ - handlers: make(map[string]Handler), - id: stringid.GenerateRandomID(), - Stdout: os.Stdout, - Stderr: os.Stderr, - Stdin: os.Stdin, - Logging: true, - } - eng.Register("commands", func(job *Job) error { - for _, name := range eng.commands() { - job.Printf("%s\n", name) - } - return nil - }) - // Copy existing global handlers - for k, v := range globalHandlers { - eng.handlers[k] = v - } - return eng -} - -func (eng *Engine) String() string { - return fmt.Sprintf("%s", eng.id[:8]) -} - -// Commands returns a list of all currently registered commands, -// sorted alphabetically. -func (eng *Engine) commands() []string { - names := make([]string, 0, len(eng.handlers)) - for name := range eng.handlers { - names = append(names, name) - } - sort.Strings(names) - return names -} - -// Job creates a new job which can later be executed. -// This function mimics `Command` from the standard os/exec package. -func (eng *Engine) Job(name string, args ...string) *Job { - job := &Job{ - Eng: eng, - Name: name, - Args: args, - Stdin: NewInput(), - Stdout: NewOutput(), - Stderr: NewOutput(), - env: &Env{}, - closeIO: true, - - cancelled: make(chan struct{}), - } - if eng.Logging { - job.Stderr.Add(ioutils.NopWriteCloser(eng.Stderr)) - } - - // Catchall is shadowed by specific Register. - if handler, exists := eng.handlers[name]; exists { - job.handler = handler - } else if eng.catchall != nil && name != "" { - // empty job names are illegal, catchall or not. - job.handler = eng.catchall - } - return job -} - -// OnShutdown registers a new callback to be called by Shutdown. -// This is typically used by services to perform cleanup. -func (eng *Engine) OnShutdown(h func()) { - eng.l.Lock() - eng.onShutdown = append(eng.onShutdown, h) - eng.shutdownWait.Add(1) - eng.l.Unlock() -} - -// Shutdown permanently shuts down eng as follows: -// - It refuses all new jobs, permanently. -// - It waits for all active jobs to complete (with no timeout) -// - It calls all shutdown handlers concurrently (if any) -// - It returns when all handlers complete, or after 15 seconds, -// whichever happens first. -func (eng *Engine) Shutdown() { - eng.l.Lock() - if eng.shutdown { - eng.l.Unlock() - eng.shutdownWait.Wait() - return - } - eng.shutdown = true - eng.l.Unlock() - // We don't need to protect the rest with a lock, to allow - // for other calls to immediately fail with "shutdown" instead - // of hanging for 15 seconds. - // This requires all concurrent calls to check for shutdown, otherwise - // it might cause a race. - - // Wait for all jobs to complete. - // Timeout after 5 seconds. - tasksDone := make(chan struct{}) - go func() { - eng.tasks.Wait() - close(tasksDone) - }() - select { - case <-time.After(time.Second * 5): - case <-tasksDone: - } - - // Call shutdown handlers, if any. - // Timeout after 10 seconds. - for _, h := range eng.onShutdown { - go func(h func()) { - h() - eng.shutdownWait.Done() - }(h) - } - done := make(chan struct{}) - go func() { - eng.shutdownWait.Wait() - close(done) - }() - select { - case <-time.After(time.Second * 10): - case <-done: - } - return -} - -// IsShutdown returns true if the engine is in the process -// of shutting down, or already shut down. -// Otherwise it returns false. -func (eng *Engine) IsShutdown() bool { - eng.l.RLock() - defer eng.l.RUnlock() - return eng.shutdown -} - -// ParseJob creates a new job from a text description using a shell-like syntax. -// -// The following syntax is used to parse `input`: -// -// * Words are separated using standard whitespaces as separators. -// * Quotes and backslashes are not interpreted. -// * Words of the form 'KEY=[VALUE]' are added to the job environment. -// * All other words are added to the job arguments. -// -// For example: -// -// job, _ := eng.ParseJob("VERBOSE=1 echo hello TEST=true world") -// -// The resulting job will have: -// job.Args={"echo", "hello", "world"} -// job.Env={"VERBOSE":"1", "TEST":"true"} -// -func (eng *Engine) ParseJob(input string) (*Job, error) { - // FIXME: use a full-featured command parser - scanner := bufio.NewScanner(strings.NewReader(input)) - scanner.Split(bufio.ScanWords) - var ( - cmd []string - env Env - ) - for scanner.Scan() { - word := scanner.Text() - kv := strings.SplitN(word, "=", 2) - if len(kv) == 2 { - env.Set(kv[0], kv[1]) - } else { - cmd = append(cmd, word) - } - } - if len(cmd) == 0 { - return nil, fmt.Errorf("empty command: '%s'", input) - } - job := eng.Job(cmd[0], cmd[1:]...) - job.Env().Init(&env) - return job, nil -} diff --git a/engine/engine_test.go b/engine/engine_test.go deleted file mode 100644 index a6ff62c8bea33..0000000000000 --- a/engine/engine_test.go +++ /dev/null @@ -1,236 +0,0 @@ -package engine - -import ( - "bytes" - "strings" - "testing" - - "github.com/docker/docker/pkg/ioutils" -) - -func TestRegister(t *testing.T) { - if err := Register("dummy1", nil); err != nil { - t.Fatal(err) - } - - if err := Register("dummy1", nil); err == nil { - t.Fatalf("Expecting error, got none") - } - // Register is global so let's cleanup to avoid conflicts - defer unregister("dummy1") - - eng := New() - - //Should fail because global handlers are copied - //at the engine creation - if err := eng.Register("dummy1", nil); err == nil { - t.Fatalf("Expecting error, got none") - } - - if err := eng.Register("dummy2", nil); err != nil { - t.Fatal(err) - } - - if err := eng.Register("dummy2", nil); err == nil { - t.Fatalf("Expecting error, got none") - } - defer unregister("dummy2") -} - -func TestJob(t *testing.T) { - eng := New() - job1 := eng.Job("dummy1", "--level=awesome") - - if job1.handler != nil { - t.Fatalf("job1.handler should be empty") - } - - h := func(j *Job) error { - j.Printf("%s\n", j.Name) - return nil - } - - eng.Register("dummy2", h) - defer unregister("dummy2") - job2 := eng.Job("dummy2", "--level=awesome") - - if job2.handler == nil { - t.Fatalf("job2.handler shouldn't be nil") - } - - if job2.handler(job2) != nil { - t.Fatalf("handler dummy2 was not found in job2") - } -} - -func TestEngineShutdown(t *testing.T) { - eng := New() - if eng.IsShutdown() { - t.Fatalf("Engine should not show as shutdown") - } - eng.Shutdown() - if !eng.IsShutdown() { - t.Fatalf("Engine should show as shutdown") - } -} - -func TestEngineCommands(t *testing.T) { - eng := New() - handler := func(job *Job) error { return nil } - eng.Register("foo", handler) - eng.Register("bar", handler) - eng.Register("echo", handler) - eng.Register("die", handler) - var output bytes.Buffer - commands := eng.Job("commands") - commands.Stdout.Add(&output) - commands.Run() - expected := "bar\ncommands\ndie\necho\nfoo\n" - if result := output.String(); result != expected { - t.Fatalf("Unexpected output:\nExpected = %v\nResult = %v\n", expected, result) - } -} - -func TestEngineString(t *testing.T) { - eng1 := New() - eng2 := New() - s1 := eng1.String() - s2 := eng2.String() - if eng1 == eng2 { - t.Fatalf("Different engines should have different names (%v == %v)", s1, s2) - } -} - -func TestParseJob(t *testing.T) { - eng := New() - // Verify that the resulting job calls to the right place - var called bool - eng.Register("echo", func(job *Job) error { - called = true - return nil - }) - input := "echo DEBUG=1 hello world VERBOSITY=42" - job, err := eng.ParseJob(input) - if err != nil { - t.Fatal(err) - } - if job.Name != "echo" { - t.Fatalf("Invalid job name: %v", job.Name) - } - if strings.Join(job.Args, ":::") != "hello:::world" { - t.Fatalf("Invalid job args: %v", job.Args) - } - if job.Env().Get("DEBUG") != "1" { - t.Fatalf("Invalid job env: %v", job.Env) - } - if job.Env().Get("VERBOSITY") != "42" { - t.Fatalf("Invalid job env: %v", job.Env) - } - if len(job.Env().Map()) != 2 { - t.Fatalf("Invalid job env: %v", job.Env) - } - if err := job.Run(); err != nil { - t.Fatal(err) - } - if !called { - t.Fatalf("Job was not called") - } -} - -func TestCatchallEmptyName(t *testing.T) { - eng := New() - var called bool - eng.RegisterCatchall(func(job *Job) error { - called = true - return nil - }) - err := eng.Job("").Run() - if err == nil { - t.Fatalf("Engine.Job(\"\").Run() should return an error") - } - if called { - t.Fatalf("Engine.Job(\"\").Run() should return an error") - } -} - -// Ensure that a job within a job both using the same underlying standard -// output writer does not close the output of the outer job when the inner -// job's stdout is wrapped with a NopCloser. When not wrapped, it should -// close the outer job's output. -func TestNestedJobSharedOutput(t *testing.T) { - var ( - outerHandler Handler - innerHandler Handler - wrapOutput bool - ) - - outerHandler = func(job *Job) error { - job.Stdout.Write([]byte("outer1")) - - innerJob := job.Eng.Job("innerJob") - - if wrapOutput { - innerJob.Stdout.Add(ioutils.NopWriteCloser(job.Stdout)) - } else { - innerJob.Stdout.Add(job.Stdout) - } - - if err := innerJob.Run(); err != nil { - t.Fatal(err) - } - - // If wrapOutput was *false* this write will do nothing. - // FIXME (jlhawn): It should cause an error to write to - // closed output. - job.Stdout.Write([]byte(" outer2")) - - return nil - } - - innerHandler = func(job *Job) error { - job.Stdout.Write([]byte(" inner")) - - return nil - } - - eng := New() - eng.Register("outerJob", outerHandler) - eng.Register("innerJob", innerHandler) - - // wrapOutput starts *false* so the expected - // output of running the outer job will be: - // - // "outer1 inner" - // - outBuf := new(bytes.Buffer) - outerJob := eng.Job("outerJob") - outerJob.Stdout.Add(outBuf) - - if err := outerJob.Run(); err != nil { - t.Fatal(err) - } - - expectedOutput := "outer1 inner" - if outBuf.String() != expectedOutput { - t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String()) - } - - // Set wrapOutput to true so that the expected - // output of running the outer job will be: - // - // "outer1 inner outer2" - // - wrapOutput = true - outBuf.Reset() - outerJob = eng.Job("outerJob") - outerJob.Stdout.Add(outBuf) - - if err := outerJob.Run(); err != nil { - t.Fatal(err) - } - - expectedOutput = "outer1 inner outer2" - if outBuf.String() != expectedOutput { - t.Fatalf("expected job output to be %q, got %q", expectedOutput, outBuf.String()) - } -} diff --git a/engine/env.go b/engine/env.go deleted file mode 100644 index 107ae4a0d9160..0000000000000 --- a/engine/env.go +++ /dev/null @@ -1,313 +0,0 @@ -package engine - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "strconv" - "strings" - "time" - - "github.com/docker/docker/pkg/ioutils" -) - -type Env []string - -// Get returns the last value associated with the given key. If there are no -// values associated with the key, Get returns the empty string. -func (env *Env) Get(key string) (value string) { - // not using Map() because of the extra allocations https://github.com/docker/docker/pull/7488#issuecomment-51638315 - for _, kv := range *env { - if strings.Index(kv, "=") == -1 { - continue - } - parts := strings.SplitN(kv, "=", 2) - if parts[0] != key { - continue - } - if len(parts) < 2 { - value = "" - } else { - value = parts[1] - } - } - return -} - -func (env *Env) Exists(key string) bool { - _, exists := env.Map()[key] - return exists -} - -// Len returns the number of keys in the environment. -// Note that len(env) might be different from env.Len(), -// because the same key might be set multiple times. -func (env *Env) Len() int { - return len(env.Map()) -} - -func (env *Env) Init(src *Env) { - (*env) = make([]string, 0, len(*src)) - for _, val := range *src { - (*env) = append((*env), val) - } -} - -func (env *Env) GetBool(key string) (value bool) { - s := strings.ToLower(strings.Trim(env.Get(key), " \t")) - if s == "" || s == "0" || s == "no" || s == "false" || s == "none" { - return false - } - return true -} - -func (env *Env) SetBool(key string, value bool) { - if value { - env.Set(key, "1") - } else { - env.Set(key, "0") - } -} - -func (env *Env) GetTime(key string) (time.Time, error) { - t, err := time.Parse(time.RFC3339Nano, env.Get(key)) - return t, err -} - -func (env *Env) SetTime(key string, t time.Time) { - env.Set(key, t.Format(time.RFC3339Nano)) -} - -func (env *Env) GetInt(key string) int { - return int(env.GetInt64(key)) -} - -func (env *Env) GetInt64(key string) int64 { - s := strings.Trim(env.Get(key), " \t") - val, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return 0 - } - return val -} - -func (env *Env) SetInt(key string, value int) { - env.Set(key, fmt.Sprintf("%d", value)) -} - -func (env *Env) SetInt64(key string, value int64) { - env.Set(key, fmt.Sprintf("%d", value)) -} - -// Returns nil if key not found -func (env *Env) GetList(key string) []string { - sval := env.Get(key) - if sval == "" { - return nil - } - l := make([]string, 0, 1) - if err := json.Unmarshal([]byte(sval), &l); err != nil { - l = append(l, sval) - } - return l -} - -func (env *Env) GetSubEnv(key string) *Env { - sval := env.Get(key) - if sval == "" { - return nil - } - buf := bytes.NewBufferString(sval) - var sub Env - if err := sub.Decode(buf); err != nil { - return nil - } - return &sub -} - -func (env *Env) SetSubEnv(key string, sub *Env) error { - var buf bytes.Buffer - if err := sub.Encode(&buf); err != nil { - return err - } - env.Set(key, string(buf.Bytes())) - return nil -} - -func (env *Env) GetJson(key string, iface interface{}) error { - sval := env.Get(key) - if sval == "" { - return nil - } - return json.Unmarshal([]byte(sval), iface) -} - -func (env *Env) SetJson(key string, value interface{}) error { - sval, err := json.Marshal(value) - if err != nil { - return err - } - env.Set(key, string(sval)) - return nil -} - -func (env *Env) SetList(key string, value []string) error { - return env.SetJson(key, value) -} - -func (env *Env) Set(key, value string) { - *env = append(*env, key+"="+value) -} - -func NewDecoder(src io.Reader) *Decoder { - return &Decoder{ - json.NewDecoder(src), - } -} - -type Decoder struct { - *json.Decoder -} - -func (decoder *Decoder) Decode() (*Env, error) { - m := make(map[string]interface{}) - if err := decoder.Decoder.Decode(&m); err != nil { - return nil, err - } - env := &Env{} - for key, value := range m { - env.SetAuto(key, value) - } - return env, nil -} - -// DecodeEnv decodes `src` as a json dictionary, and adds -// each decoded key-value pair to the environment. -// -// If `src` cannot be decoded as a json dictionary, an error -// is returned. -func (env *Env) Decode(src io.Reader) error { - m := make(map[string]interface{}) - d := json.NewDecoder(src) - // We need this or we'll lose data when we decode int64 in json - d.UseNumber() - if err := d.Decode(&m); err != nil { - return err - } - for k, v := range m { - env.SetAuto(k, v) - } - return nil -} - -func (env *Env) SetAuto(k string, v interface{}) { - // Issue 7941 - if the value in the incoming JSON is null then treat it - // as if they never specified the property at all. - if v == nil { - return - } - - // FIXME: we fix-convert float values to int, because - // encoding/json decodes integers to float64, but cannot encode them back. - // (See https://golang.org/src/pkg/encoding/json/decode.go#L46) - if fval, ok := v.(float64); ok { - env.SetInt64(k, int64(fval)) - } else if sval, ok := v.(string); ok { - env.Set(k, sval) - } else if val, err := json.Marshal(v); err == nil { - env.Set(k, string(val)) - } else { - env.Set(k, fmt.Sprintf("%v", v)) - } -} - -func changeFloats(v interface{}) interface{} { - switch v := v.(type) { - case float64: - return int(v) - case map[string]interface{}: - for key, val := range v { - v[key] = changeFloats(val) - } - case []interface{}: - for idx, val := range v { - v[idx] = changeFloats(val) - } - } - return v -} - -func (env *Env) Encode(dst io.Writer) error { - m := make(map[string]interface{}) - for k, v := range env.Map() { - var val interface{} - if err := json.Unmarshal([]byte(v), &val); err == nil { - // FIXME: we fix-convert float values to int, because - // encoding/json decodes integers to float64, but cannot encode them back. - // (See https://golang.org/src/pkg/encoding/json/decode.go#L46) - m[k] = changeFloats(val) - } else { - m[k] = v - } - } - if err := json.NewEncoder(dst).Encode(&m); err != nil { - return err - } - return nil -} - -func (env *Env) WriteTo(dst io.Writer) (int64, error) { - wc := ioutils.NewWriteCounter(dst) - err := env.Encode(wc) - return wc.Count, err -} - -func (env *Env) Import(src interface{}) (err error) { - defer func() { - if err != nil { - err = fmt.Errorf("ImportEnv: %s", err) - } - }() - var buf bytes.Buffer - if err := json.NewEncoder(&buf).Encode(src); err != nil { - return err - } - if err := env.Decode(&buf); err != nil { - return err - } - return nil -} - -func (env *Env) Map() map[string]string { - m := make(map[string]string) - for _, kv := range *env { - parts := strings.SplitN(kv, "=", 2) - m[parts[0]] = parts[1] - } - return m -} - -// MultiMap returns a representation of env as a -// map of string arrays, keyed by string. -// This is the same structure as http headers for example, -// which allow each key to have multiple values. -func (env *Env) MultiMap() map[string][]string { - m := make(map[string][]string) - for _, kv := range *env { - parts := strings.SplitN(kv, "=", 2) - m[parts[0]] = append(m[parts[0]], parts[1]) - } - return m -} - -// InitMultiMap removes all values in env, then initializes -// new values from the contents of m. -func (env *Env) InitMultiMap(m map[string][]string) { - (*env) = make([]string, 0, len(m)) - for k, vals := range m { - for _, v := range vals { - env.Set(k, v) - } - } -} diff --git a/engine/env_test.go b/engine/env_test.go deleted file mode 100644 index 1398275b2b364..0000000000000 --- a/engine/env_test.go +++ /dev/null @@ -1,366 +0,0 @@ -package engine - -import ( - "bytes" - "encoding/json" - "testing" - "time" - - "github.com/docker/docker/pkg/stringutils" -) - -func TestEnvLenZero(t *testing.T) { - env := &Env{} - if env.Len() != 0 { - t.Fatalf("%d", env.Len()) - } -} - -func TestEnvLenNotZero(t *testing.T) { - env := &Env{} - env.Set("foo", "bar") - env.Set("ga", "bu") - if env.Len() != 2 { - t.Fatalf("%d", env.Len()) - } -} - -func TestEnvLenDup(t *testing.T) { - env := &Env{ - "foo=bar", - "foo=baz", - "a=b", - } - // len(env) != env.Len() - if env.Len() != 2 { - t.Fatalf("%d", env.Len()) - } -} - -func TestEnvGetDup(t *testing.T) { - env := &Env{ - "foo=bar", - "foo=baz", - "foo=bif", - } - expected := "bif" - if v := env.Get("foo"); v != expected { - t.Fatalf("expect %q, got %q", expected, v) - } -} - -func TestNewJob(t *testing.T) { - job := mkJob(t, "dummy", "--level=awesome") - if job.Name != "dummy" { - t.Fatalf("Wrong job name: %s", job.Name) - } - if len(job.Args) != 1 { - t.Fatalf("Wrong number of job arguments: %d", len(job.Args)) - } - if job.Args[0] != "--level=awesome" { - t.Fatalf("Wrong job arguments: %s", job.Args[0]) - } -} - -func TestSetenv(t *testing.T) { - job := mkJob(t, "dummy") - job.Setenv("foo", "bar") - if val := job.Getenv("foo"); val != "bar" { - t.Fatalf("Getenv returns incorrect value: %s", val) - } - - job.Setenv("bar", "") - if val := job.Getenv("bar"); val != "" { - t.Fatalf("Getenv returns incorrect value: %s", val) - } - if val := job.Getenv("nonexistent"); val != "" { - t.Fatalf("Getenv returns incorrect value: %s", val) - } -} - -func TestDecodeEnv(t *testing.T) { - job := mkJob(t, "dummy") - type tmp struct { - Id1 int64 - Id2 int64 - } - body := []byte("{\"tags\":{\"Id1\":123, \"Id2\":1234567}}") - if err := job.DecodeEnv(bytes.NewBuffer(body)); err != nil { - t.Fatalf("DecodeEnv failed: %v", err) - } - mytag := tmp{} - if val := job.GetenvJson("tags", &mytag); val != nil { - t.Fatalf("GetenvJson returns incorrect value: %s", val) - } - - if mytag.Id1 != 123 || mytag.Id2 != 1234567 { - t.Fatal("Get wrong values set by job.DecodeEnv") - } -} - -func TestSetenvBool(t *testing.T) { - job := mkJob(t, "dummy") - job.SetenvBool("foo", true) - if val := job.GetenvBool("foo"); !val { - t.Fatalf("GetenvBool returns incorrect value: %t", val) - } - - job.SetenvBool("bar", false) - if val := job.GetenvBool("bar"); val { - t.Fatalf("GetenvBool returns incorrect value: %t", val) - } - - if val := job.GetenvBool("nonexistent"); val { - t.Fatalf("GetenvBool returns incorrect value: %t", val) - } -} - -func TestSetenvTime(t *testing.T) { - job := mkJob(t, "dummy") - - now := time.Now() - job.SetenvTime("foo", now) - if val, err := job.GetenvTime("foo"); err != nil { - t.Fatalf("GetenvTime failed to parse: %v", err) - } else { - nowStr := now.Format(time.RFC3339) - valStr := val.Format(time.RFC3339) - if nowStr != valStr { - t.Fatalf("GetenvTime returns incorrect value: %s, Expected: %s", valStr, nowStr) - } - } - - job.Setenv("bar", "Obviously I'm not a date") - if val, err := job.GetenvTime("bar"); err == nil { - t.Fatalf("GetenvTime was supposed to fail, instead returned: %s", val) - } -} - -func TestSetenvInt(t *testing.T) { - job := mkJob(t, "dummy") - - job.SetenvInt("foo", -42) - if val := job.GetenvInt("foo"); val != -42 { - t.Fatalf("GetenvInt returns incorrect value: %d", val) - } - - job.SetenvInt("bar", 42) - if val := job.GetenvInt("bar"); val != 42 { - t.Fatalf("GetenvInt returns incorrect value: %d", val) - } - if val := job.GetenvInt("nonexistent"); val != 0 { - t.Fatalf("GetenvInt returns incorrect value: %d", val) - } -} - -func TestSetenvList(t *testing.T) { - job := mkJob(t, "dummy") - - job.SetenvList("foo", []string{"bar"}) - if val := job.GetenvList("foo"); len(val) != 1 || val[0] != "bar" { - t.Fatalf("GetenvList returns incorrect value: %v", val) - } - - job.SetenvList("bar", nil) - if val := job.GetenvList("bar"); val != nil { - t.Fatalf("GetenvList returns incorrect value: %v", val) - } - if val := job.GetenvList("nonexistent"); val != nil { - t.Fatalf("GetenvList returns incorrect value: %v", val) - } -} - -func TestEnviron(t *testing.T) { - job := mkJob(t, "dummy") - job.Setenv("foo", "bar") - val, exists := job.Environ()["foo"] - if !exists { - t.Fatalf("foo not found in the environ") - } - if val != "bar" { - t.Fatalf("bar not found in the environ") - } -} - -func TestMultiMap(t *testing.T) { - e := &Env{} - e.Set("foo", "bar") - e.Set("bar", "baz") - e.Set("hello", "world") - m := e.MultiMap() - e2 := &Env{} - e2.Set("old_key", "something something something") - e2.InitMultiMap(m) - if v := e2.Get("old_key"); v != "" { - t.Fatalf("%#v", v) - } - if v := e2.Get("bar"); v != "baz" { - t.Fatalf("%#v", v) - } - if v := e2.Get("hello"); v != "world" { - t.Fatalf("%#v", v) - } -} - -func testMap(l int) [][2]string { - res := make([][2]string, l) - for i := 0; i < l; i++ { - t := [2]string{stringutils.GenerateRandomAsciiString(5), stringutils.GenerateRandomAsciiString(20)} - res[i] = t - } - return res -} - -func BenchmarkSet(b *testing.B) { - fix := testMap(100) - b.ResetTimer() - for i := 0; i < b.N; i++ { - env := &Env{} - for _, kv := range fix { - env.Set(kv[0], kv[1]) - } - } -} - -func BenchmarkSetJson(b *testing.B) { - fix := testMap(100) - type X struct { - f string - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - env := &Env{} - for _, kv := range fix { - if err := env.SetJson(kv[0], X{kv[1]}); err != nil { - b.Fatal(err) - } - } - } -} - -func BenchmarkGet(b *testing.B) { - fix := testMap(100) - env := &Env{} - for _, kv := range fix { - env.Set(kv[0], kv[1]) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - for _, kv := range fix { - env.Get(kv[0]) - } - } -} - -func BenchmarkGetJson(b *testing.B) { - fix := testMap(100) - env := &Env{} - type X struct { - f string - } - for _, kv := range fix { - env.SetJson(kv[0], X{kv[1]}) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - for _, kv := range fix { - if err := env.GetJson(kv[0], &X{}); err != nil { - b.Fatal(err) - } - } - } -} - -func BenchmarkEncode(b *testing.B) { - fix := testMap(100) - env := &Env{} - type X struct { - f string - } - // half a json - for i, kv := range fix { - if i%2 != 0 { - if err := env.SetJson(kv[0], X{kv[1]}); err != nil { - b.Fatal(err) - } - continue - } - env.Set(kv[0], kv[1]) - } - var writer bytes.Buffer - b.ResetTimer() - for i := 0; i < b.N; i++ { - env.Encode(&writer) - writer.Reset() - } -} - -func BenchmarkDecode(b *testing.B) { - fix := testMap(100) - env := &Env{} - type X struct { - f string - } - // half a json - for i, kv := range fix { - if i%2 != 0 { - if err := env.SetJson(kv[0], X{kv[1]}); err != nil { - b.Fatal(err) - } - continue - } - env.Set(kv[0], kv[1]) - } - var writer bytes.Buffer - env.Encode(&writer) - denv := &Env{} - reader := bytes.NewReader(writer.Bytes()) - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := denv.Decode(reader) - if err != nil { - b.Fatal(err) - } - reader.Seek(0, 0) - } -} - -func TestLongNumbers(t *testing.T) { - type T struct { - TestNum int64 - } - v := T{67108864} - var buf bytes.Buffer - e := &Env{} - e.SetJson("Test", v) - if err := e.Encode(&buf); err != nil { - t.Fatal(err) - } - res := make(map[string]T) - if err := json.Unmarshal(buf.Bytes(), &res); err != nil { - t.Fatal(err) - } - if res["Test"].TestNum != v.TestNum { - t.Fatalf("TestNum %d, expected %d", res["Test"].TestNum, v.TestNum) - } -} - -func TestLongNumbersArray(t *testing.T) { - type T struct { - TestNum []int64 - } - v := T{[]int64{67108864}} - var buf bytes.Buffer - e := &Env{} - e.SetJson("Test", v) - if err := e.Encode(&buf); err != nil { - t.Fatal(err) - } - res := make(map[string]T) - if err := json.Unmarshal(buf.Bytes(), &res); err != nil { - t.Fatal(err) - } - if res["Test"].TestNum[0] != v.TestNum[0] { - t.Fatalf("TestNum %d, expected %d", res["Test"].TestNum, v.TestNum) - } -} diff --git a/engine/hack.go b/engine/hack.go deleted file mode 100644 index 10595ce2b11ec..0000000000000 --- a/engine/hack.go +++ /dev/null @@ -1,21 +0,0 @@ -package engine - -type Hack map[string]interface{} - -func (eng *Engine) HackGetGlobalVar(key string) interface{} { - if eng.hack == nil { - return nil - } - val, exists := eng.hack[key] - if !exists { - return nil - } - return val -} - -func (eng *Engine) HackSetGlobalVar(key string, val interface{}) { - if eng.hack == nil { - eng.hack = make(Hack) - } - eng.hack[key] = val -} diff --git a/engine/helpers_test.go b/engine/helpers_test.go deleted file mode 100644 index cfa11da7cdefd..0000000000000 --- a/engine/helpers_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package engine - -import ( - "testing" -) - -var globalTestID string - -func mkJob(t *testing.T, name string, args ...string) *Job { - return New().Job(name, args...) -} diff --git a/engine/http.go b/engine/http.go deleted file mode 100644 index 7e4dcd7bb4b3d..0000000000000 --- a/engine/http.go +++ /dev/null @@ -1,42 +0,0 @@ -package engine - -import ( - "net/http" - "path" -) - -// ServeHTTP executes a job as specified by the http request `r`, and sends the -// result as an http response. -// This method allows an Engine instance to be passed as a standard http.Handler interface. -// -// Note that the protocol used in this method is a convenience wrapper and is not the canonical -// implementation of remote job execution. This is because HTTP/1 does not handle stream multiplexing, -// and so cannot differentiate stdout from stderr. Additionally, headers cannot be added to a response -// once data has been written to the body, which makes it inconvenient to return metadata such -// as the exit status. -// -func (eng *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) { - var ( - jobName = path.Base(r.URL.Path) - jobArgs, exists = r.URL.Query()["a"] - ) - if !exists { - jobArgs = []string{} - } - w.Header().Set("Job-Name", jobName) - for _, arg := range jobArgs { - w.Header().Add("Job-Args", arg) - } - job := eng.Job(jobName, jobArgs...) - job.Stdout.Add(w) - job.Stderr.Add(w) - // FIXME: distinguish job status from engine error in Run() - // The former should be passed as a special header, the former - // should cause a 500 status - w.WriteHeader(http.StatusOK) - // The exit status cannot be sent reliably with HTTP1, because headers - // can only be sent before the body. - // (we could possibly use http footers via chunked encoding, but I couldn't find - // how to use them in net/http) - job.Run() -} diff --git a/engine/job.go b/engine/job.go deleted file mode 100644 index 12acdc9334a78..0000000000000 --- a/engine/job.go +++ /dev/null @@ -1,222 +0,0 @@ -package engine - -import ( - "bytes" - "fmt" - "io" - "strings" - "sync" - "time" - - "github.com/Sirupsen/logrus" -) - -// A job is the fundamental unit of work in the docker engine. -// Everything docker can do should eventually be exposed as a job. -// For example: execute a process in a container, create a new container, -// download an archive from the internet, serve the http api, etc. -// -// The job API is designed after unix processes: a job has a name, arguments, -// environment variables, standard streams for input, output and error. -type Job struct { - Eng *Engine - Name string - Args []string - env *Env - Stdout *Output - Stderr *Output - Stdin *Input - handler Handler - end time.Time - closeIO bool - - // When closed, the job has been cancelled. - // Note: not all jobs implement cancellation. - // See Job.Cancel() and Job.WaitCancelled() - cancelled chan struct{} - cancelOnce sync.Once -} - -// Run executes the job and blocks until the job completes. -// If the job fails it returns an error -func (job *Job) Run() (err error) { - defer func() { - // Wait for all background tasks to complete - if job.closeIO { - if err := job.Stdout.Close(); err != nil { - logrus.Error(err) - } - if err := job.Stderr.Close(); err != nil { - logrus.Error(err) - } - if err := job.Stdin.Close(); err != nil { - logrus.Error(err) - } - } - }() - - if job.Eng.IsShutdown() && !job.GetenvBool("overrideShutdown") { - return fmt.Errorf("engine is shutdown") - } - // FIXME: this is a temporary workaround to avoid Engine.Shutdown - // waiting 5 seconds for server/api.ServeApi to complete (which it never will) - // everytime the daemon is cleanly restarted. - // The permanent fix is to implement Job.Stop and Job.OnStop so that - // ServeApi can cooperate and terminate cleanly. - if job.Name != "serveapi" { - job.Eng.l.Lock() - job.Eng.tasks.Add(1) - job.Eng.l.Unlock() - defer job.Eng.tasks.Done() - } - // FIXME: make this thread-safe - // FIXME: implement wait - if !job.end.IsZero() { - return fmt.Errorf("%s: job has already completed", job.Name) - } - // Log beginning and end of the job - if job.Eng.Logging { - logrus.Infof("+job %s", job.CallString()) - defer func() { - okerr := "OK" - if err != nil { - okerr = fmt.Sprintf("ERR: %s", err) - } - logrus.Infof("-job %s %s", job.CallString(), okerr) - }() - } - - if job.handler == nil { - return fmt.Errorf("%s: command not found", job.Name) - } - - var errorMessage = bytes.NewBuffer(nil) - job.Stderr.Add(errorMessage) - - err = job.handler(job) - job.end = time.Now() - - return -} - -func (job *Job) CallString() string { - return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", ")) -} - -func (job *Job) Env() *Env { - return job.env -} - -func (job *Job) EnvExists(key string) (value bool) { - return job.env.Exists(key) -} - -func (job *Job) Getenv(key string) (value string) { - return job.env.Get(key) -} - -func (job *Job) GetenvBool(key string) (value bool) { - return job.env.GetBool(key) -} - -func (job *Job) SetenvBool(key string, value bool) { - job.env.SetBool(key, value) -} - -func (job *Job) GetenvTime(key string) (value time.Time, err error) { - return job.env.GetTime(key) -} - -func (job *Job) SetenvTime(key string, value time.Time) { - job.env.SetTime(key, value) -} - -func (job *Job) GetenvSubEnv(key string) *Env { - return job.env.GetSubEnv(key) -} - -func (job *Job) SetenvSubEnv(key string, value *Env) error { - return job.env.SetSubEnv(key, value) -} - -func (job *Job) GetenvInt64(key string) int64 { - return job.env.GetInt64(key) -} - -func (job *Job) GetenvInt(key string) int { - return job.env.GetInt(key) -} - -func (job *Job) SetenvInt64(key string, value int64) { - job.env.SetInt64(key, value) -} - -func (job *Job) SetenvInt(key string, value int) { - job.env.SetInt(key, value) -} - -// Returns nil if key not found -func (job *Job) GetenvList(key string) []string { - return job.env.GetList(key) -} - -func (job *Job) GetenvJson(key string, iface interface{}) error { - return job.env.GetJson(key, iface) -} - -func (job *Job) SetenvJson(key string, value interface{}) error { - return job.env.SetJson(key, value) -} - -func (job *Job) SetenvList(key string, value []string) error { - return job.env.SetJson(key, value) -} - -func (job *Job) Setenv(key, value string) { - job.env.Set(key, value) -} - -// DecodeEnv decodes `src` as a json dictionary, and adds -// each decoded key-value pair to the environment. -// -// If `src` cannot be decoded as a json dictionary, an error -// is returned. -func (job *Job) DecodeEnv(src io.Reader) error { - return job.env.Decode(src) -} - -func (job *Job) EncodeEnv(dst io.Writer) error { - return job.env.Encode(dst) -} - -func (job *Job) ImportEnv(src interface{}) (err error) { - return job.env.Import(src) -} - -func (job *Job) Environ() map[string]string { - return job.env.Map() -} - -func (job *Job) Printf(format string, args ...interface{}) (n int, err error) { - return fmt.Fprintf(job.Stdout, format, args...) -} - -func (job *Job) Errorf(format string, args ...interface{}) (n int, err error) { - return fmt.Fprintf(job.Stderr, format, args...) -} - -func (job *Job) SetCloseIO(val bool) { - job.closeIO = val -} - -// When called, causes the Job.WaitCancelled channel to unblock. -func (job *Job) Cancel() { - job.cancelOnce.Do(func() { - close(job.cancelled) - }) -} - -// Returns a channel which is closed ("never blocks") when the job is cancelled. -func (job *Job) WaitCancelled() <-chan struct{} { - return job.cancelled -} diff --git a/engine/job_test.go b/engine/job_test.go deleted file mode 100644 index 76135e6e675b4..0000000000000 --- a/engine/job_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package engine - -import ( - "bytes" - "errors" - "fmt" - "testing" -) - -func TestJobOK(t *testing.T) { - eng := New() - eng.Register("return_ok", func(job *Job) error { return nil }) - err := eng.Job("return_ok").Run() - if err != nil { - t.Fatalf("Expected: err=%v\nReceived: err=%v", nil, err) - } -} - -func TestJobErr(t *testing.T) { - eng := New() - eng.Register("return_err", func(job *Job) error { return errors.New("return_err") }) - err := eng.Job("return_err").Run() - if err == nil { - t.Fatalf("When a job returns error, Run() should return an error") - } -} - -func TestJobStdoutString(t *testing.T) { - eng := New() - // FIXME: test multiple combinations of output and status - eng.Register("say_something_in_stdout", func(job *Job) error { - job.Printf("Hello world\n") - return nil - }) - - job := eng.Job("say_something_in_stdout") - var outputBuffer = bytes.NewBuffer(nil) - job.Stdout.Add(outputBuffer) - if err := job.Run(); err != nil { - t.Fatal(err) - } - fmt.Println(outputBuffer) - var output = Tail(outputBuffer, 1) - if expectedOutput := "Hello world"; output != expectedOutput { - t.Fatalf("Stdout last line:\nExpected: %v\nReceived: %v", expectedOutput, output) - } -} diff --git a/engine/shutdown_test.go b/engine/shutdown_test.go deleted file mode 100644 index d2ef0339de399..0000000000000 --- a/engine/shutdown_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package engine - -import ( - "testing" - "time" -) - -func TestShutdownEmpty(t *testing.T) { - eng := New() - if eng.IsShutdown() { - t.Fatalf("IsShutdown should be false") - } - eng.Shutdown() - if !eng.IsShutdown() { - t.Fatalf("IsShutdown should be true") - } -} - -func TestShutdownAfterRun(t *testing.T) { - eng := New() - eng.Register("foo", func(job *Job) error { - return nil - }) - if err := eng.Job("foo").Run(); err != nil { - t.Fatal(err) - } - eng.Shutdown() - if err := eng.Job("foo").Run(); err == nil { - t.Fatalf("%#v", *eng) - } -} - -// An approximate and racy, but better-than-nothing test that -// -func TestShutdownDuringRun(t *testing.T) { - var ( - jobDelay time.Duration = 500 * time.Millisecond - jobDelayLow time.Duration = 100 * time.Millisecond - jobDelayHigh time.Duration = 700 * time.Millisecond - ) - eng := New() - var completed bool - eng.Register("foo", func(job *Job) error { - time.Sleep(jobDelay) - completed = true - return nil - }) - go eng.Job("foo").Run() - time.Sleep(50 * time.Millisecond) - done := make(chan struct{}) - var startShutdown time.Time - go func() { - startShutdown = time.Now() - eng.Shutdown() - close(done) - }() - time.Sleep(50 * time.Millisecond) - if err := eng.Job("foo").Run(); err == nil { - t.Fatalf("run on shutdown should fail: %#v", *eng) - } - <-done - // Verify that Shutdown() blocks for roughly 500ms, instead - // of returning almost instantly. - // - // We use >100ms to leave ample margin for race conditions between - // goroutines. It's possible (but unlikely in reasonable testing - // conditions), that this test will cause a false positive or false - // negative. But it's probably better than not having any test - // for the 99.999% of time where testing conditions are reasonable. - if d := time.Since(startShutdown); d.Nanoseconds() < jobDelayLow.Nanoseconds() { - t.Fatalf("shutdown did not block long enough: %v", d) - } else if d.Nanoseconds() > jobDelayHigh.Nanoseconds() { - t.Fatalf("shutdown blocked too long: %v", d) - } - if !completed { - t.Fatalf("job did not complete") - } -} diff --git a/engine/streams.go b/engine/streams.go deleted file mode 100644 index 2863e944876d7..0000000000000 --- a/engine/streams.go +++ /dev/null @@ -1,188 +0,0 @@ -package engine - -import ( - "bytes" - "fmt" - "io" - "strings" - "sync" - "unicode" -) - -type Output struct { - sync.Mutex - dests []io.Writer - tasks sync.WaitGroup - used bool -} - -// Tail returns the n last lines of a buffer -// stripped out of trailing white spaces, if any. -// -// if n <= 0, returns an empty string -func Tail(buffer *bytes.Buffer, n int) string { - if n <= 0 { - return "" - } - s := strings.TrimRightFunc(buffer.String(), unicode.IsSpace) - i := len(s) - 1 - for ; i >= 0 && n > 0; i-- { - if s[i] == '\n' { - n-- - if n == 0 { - break - } - } - } - // when i == -1, return the whole string which is s[0:] - return s[i+1:] -} - -// NewOutput returns a new Output object with no destinations attached. -// Writing to an empty Output will cause the written data to be discarded. -func NewOutput() *Output { - return &Output{} -} - -// Return true if something was written on this output -func (o *Output) Used() bool { - o.Lock() - defer o.Unlock() - return o.used -} - -// Add attaches a new destination to the Output. Any data subsequently written -// to the output will be written to the new destination in addition to all the others. -// This method is thread-safe. -func (o *Output) Add(dst io.Writer) { - o.Lock() - defer o.Unlock() - o.dests = append(o.dests, dst) -} - -// Set closes and remove existing destination and then attaches a new destination to -// the Output. Any data subsequently written to the output will be written to the new -// destination in addition to all the others. This method is thread-safe. -func (o *Output) Set(dst io.Writer) { - o.Close() - o.Lock() - defer o.Unlock() - o.dests = []io.Writer{dst} -} - -// AddPipe creates an in-memory pipe with io.Pipe(), adds its writing end as a destination, -// and returns its reading end for consumption by the caller. -// This is a rough equivalent similar to Cmd.StdoutPipe() in the standard os/exec package. -// This method is thread-safe. -func (o *Output) AddPipe() (io.Reader, error) { - r, w := io.Pipe() - o.Add(w) - return r, nil -} - -// Write writes the same data to all registered destinations. -// This method is thread-safe. -func (o *Output) Write(p []byte) (n int, err error) { - o.Lock() - defer o.Unlock() - o.used = true - var firstErr error - for _, dst := range o.dests { - _, err := dst.Write(p) - if err != nil && firstErr == nil { - firstErr = err - } - } - return len(p), firstErr -} - -// Close unregisters all destinations and waits for all background -// AddTail and AddString tasks to complete. -// The Close method of each destination is called if it exists. -func (o *Output) Close() error { - o.Lock() - defer o.Unlock() - var firstErr error - for _, dst := range o.dests { - if closer, ok := dst.(io.Closer); ok { - err := closer.Close() - if err != nil && firstErr == nil { - firstErr = err - } - } - } - o.tasks.Wait() - o.dests = nil - return firstErr -} - -type Input struct { - src io.Reader - sync.Mutex -} - -// NewInput returns a new Input object with no source attached. -// Reading to an empty Input will return io.EOF. -func NewInput() *Input { - return &Input{} -} - -// Read reads from the input in a thread-safe way. -func (i *Input) Read(p []byte) (n int, err error) { - i.Mutex.Lock() - defer i.Mutex.Unlock() - if i.src == nil { - return 0, io.EOF - } - return i.src.Read(p) -} - -// Closes the src -// Not thread safe on purpose -func (i *Input) Close() error { - if i.src != nil { - if closer, ok := i.src.(io.Closer); ok { - return closer.Close() - } - } - return nil -} - -// Add attaches a new source to the input. -// Add can only be called once per input. Subsequent calls will -// return an error. -func (i *Input) Add(src io.Reader) error { - i.Mutex.Lock() - defer i.Mutex.Unlock() - if i.src != nil { - return fmt.Errorf("Maximum number of sources reached: 1") - } - i.src = src - return nil -} - -// AddEnv starts a new goroutine which will decode all subsequent data -// as a stream of json-encoded objects, and point `dst` to the last -// decoded object. -// The result `env` can be queried using the type-neutral Env interface. -// It is not safe to query `env` until the Output is closed. -func (o *Output) AddEnv() (dst *Env, err error) { - src, err := o.AddPipe() - if err != nil { - return nil, err - } - dst = &Env{} - o.tasks.Add(1) - go func() { - defer o.tasks.Done() - decoder := NewDecoder(src) - for { - env, err := decoder.Decode() - if err != nil { - return - } - *dst = *env - } - }() - return dst, nil -} diff --git a/engine/streams_test.go b/engine/streams_test.go deleted file mode 100644 index c22338a32e752..0000000000000 --- a/engine/streams_test.go +++ /dev/null @@ -1,215 +0,0 @@ -package engine - -import ( - "bufio" - "bytes" - "fmt" - "io" - "io/ioutil" - "strings" - "testing" -) - -type sentinelWriteCloser struct { - calledWrite bool - calledClose bool -} - -func (w *sentinelWriteCloser) Write(p []byte) (int, error) { - w.calledWrite = true - return len(p), nil -} - -func (w *sentinelWriteCloser) Close() error { - w.calledClose = true - return nil -} - -func TestOutputAddEnv(t *testing.T) { - input := "{\"foo\": \"bar\", \"answer_to_life_the_universe_and_everything\": 42}" - o := NewOutput() - result, err := o.AddEnv() - if err != nil { - t.Fatal(err) - } - o.Write([]byte(input)) - o.Close() - if v := result.Get("foo"); v != "bar" { - t.Errorf("Expected %v, got %v", "bar", v) - } - if v := result.GetInt("answer_to_life_the_universe_and_everything"); v != 42 { - t.Errorf("Expected %v, got %v", 42, v) - } - if v := result.Get("this-value-doesnt-exist"); v != "" { - t.Errorf("Expected %v, got %v", "", v) - } -} - -func TestOutputAddClose(t *testing.T) { - o := NewOutput() - var s sentinelWriteCloser - o.Add(&s) - if err := o.Close(); err != nil { - t.Fatal(err) - } - // Write data after the output is closed. - // Write should succeed, but no destination should receive it. - if _, err := o.Write([]byte("foo bar")); err != nil { - t.Fatal(err) - } - if !s.calledClose { - t.Fatal("Output.Close() didn't close the destination") - } -} - -func TestOutputAddPipe(t *testing.T) { - var testInputs = []string{ - "hello, world!", - "One\nTwo\nThree", - "", - "A line\nThen another nl-terminated line\n", - "A line followed by an empty line\n\n", - } - for _, input := range testInputs { - expectedOutput := input - o := NewOutput() - r, err := o.AddPipe() - if err != nil { - t.Fatal(err) - } - go func(o *Output) { - if n, err := o.Write([]byte(input)); err != nil { - t.Error(err) - } else if n != len(input) { - t.Errorf("Expected %d, got %d", len(input), n) - } - if err := o.Close(); err != nil { - t.Error(err) - } - }(o) - output, err := ioutil.ReadAll(r) - if err != nil { - t.Fatal(err) - } - if string(output) != expectedOutput { - t.Errorf("Last line is not stored as return string.\nExpected: '%s'\nGot: '%s'", expectedOutput, output) - } - } -} - -func TestTail(t *testing.T) { - var tests = make(map[string][]string) - tests["hello, world!"] = []string{ - "", - "hello, world!", - "hello, world!", - "hello, world!", - } - tests["One\nTwo\nThree"] = []string{ - "", - "Three", - "Two\nThree", - "One\nTwo\nThree", - } - tests["One\nTwo\n\n\n"] = []string{ - "", - "Two", - "One\nTwo", - } - for input, outputs := range tests { - for n, expectedOutput := range outputs { - output := Tail(bytes.NewBufferString(input), n) - if output != expectedOutput { - t.Errorf("Tail n=%d returned wrong result.\nExpected: '%s'\nGot : '%s'", n, expectedOutput, output) - } - } - } -} - -func lastLine(txt string) string { - scanner := bufio.NewScanner(strings.NewReader(txt)) - var lastLine string - for scanner.Scan() { - lastLine = scanner.Text() - } - return lastLine -} - -func TestOutputAdd(t *testing.T) { - o := NewOutput() - b := &bytes.Buffer{} - o.Add(b) - input := "hello, world!" - if n, err := o.Write([]byte(input)); err != nil { - t.Fatal(err) - } else if n != len(input) { - t.Fatalf("Expected %d, got %d", len(input), n) - } - if output := b.String(); output != input { - t.Fatalf("Received wrong data from Add.\nExpected: '%s'\nGot: '%s'", input, output) - } -} - -func TestOutputWriteError(t *testing.T) { - o := NewOutput() - buf := &bytes.Buffer{} - o.Add(buf) - r, w := io.Pipe() - input := "Hello there" - expectedErr := fmt.Errorf("This is an error") - r.CloseWithError(expectedErr) - o.Add(w) - n, err := o.Write([]byte(input)) - if err != expectedErr { - t.Fatalf("Output.Write() should return the first error encountered, if any") - } - if buf.String() != input { - t.Fatalf("Output.Write() should attempt write on all destinations, even after encountering an error") - } - if n != len(input) { - t.Fatalf("Output.Write() should return the size of the input if it successfully writes to at least one destination") - } -} - -func TestInputAddEmpty(t *testing.T) { - i := NewInput() - var b bytes.Buffer - if err := i.Add(&b); err != nil { - t.Fatal(err) - } - data, err := ioutil.ReadAll(i) - if err != nil { - t.Fatal(err) - } - if len(data) > 0 { - t.Fatalf("Read from empty input should yield no data") - } -} - -func TestInputAddTwo(t *testing.T) { - i := NewInput() - var b1 bytes.Buffer - // First add should succeed - if err := i.Add(&b1); err != nil { - t.Fatal(err) - } - var b2 bytes.Buffer - // Second add should fail - if err := i.Add(&b2); err == nil { - t.Fatalf("Adding a second source should return an error") - } -} - -func TestInputAddNotEmpty(t *testing.T) { - i := NewInput() - b := bytes.NewBufferString("hello world\nabc") - expectedResult := b.String() - i.Add(b) - result, err := ioutil.ReadAll(i) - if err != nil { - t.Fatal(err) - } - if string(result) != expectedResult { - t.Fatalf("Expected: %v\nReceived: %v", expectedResult, result) - } -} diff --git a/pkg/pidfile/pidfile.go b/pkg/pidfile/pidfile.go index 21a5438799795..7cc6b964a7b15 100644 --- a/pkg/pidfile/pidfile.go +++ b/pkg/pidfile/pidfile.go @@ -24,15 +24,15 @@ func checkPidFileAlreadyExists(path string) error { return nil } -func New(path string) (file *PidFile, err error) { +func New(path string) (*PidFile, error) { if err := checkPidFileAlreadyExists(path); err != nil { return nil, err } + if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil { + return nil, err + } - file = &PidFile{path: path} - err = ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644) - - return file, err + return &PidFile{path: path}, nil } func (file PidFile) Remove() error { From f7e417ea5e26f11ec43dba64ee153765d2276f40 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 29 Apr 2015 13:56:45 +0200 Subject: [PATCH 037/321] Remove integration tests and port them to integration-cli Signed-off-by: Antonio Murdaca --- Makefile | 7 +- hack/make.sh | 2 - hack/make/test-integration | 25 - integration-cli/docker_api_containers_test.go | 182 ++++ integration-cli/docker_api_images_test.go | 24 +- integration-cli/docker_api_test.go | 25 + integration-cli/docker_cli_daemon_test.go | 2 +- integration/README.md | 23 - integration/api_test.go | 680 -------------- integration/container_test.go | 235 ----- integration/runtime_test.go | 847 ------------------ integration/utils.go | 88 -- integration/utils_test.go | 348 ------- integration/z_final_test.go | 18 - 14 files changed, 233 insertions(+), 2273 deletions(-) delete mode 100644 hack/make/test-integration create mode 100644 integration-cli/docker_api_test.go delete mode 100644 integration/README.md delete mode 100644 integration/api_test.go delete mode 100644 integration/container_test.go delete mode 100644 integration/runtime_test.go delete mode 100644 integration/utils.go delete mode 100644 integration/utils_test.go delete mode 100644 integration/z_final_test.go diff --git a/Makefile b/Makefile index b60b2a4d0004d..257fefdfeeb95 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli test-docker-py validate +.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration-cli test-docker-py validate # env vars passed through directly to Docker's build scripts # to allow things like `make DOCKER_CLIENTONLY=1 binary` easily @@ -62,14 +62,11 @@ docs-test: docs-build $(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh test: build - $(DOCKER_RUN_DOCKER) hack/make.sh binary cross test-unit test-integration test-integration-cli test-docker-py + $(DOCKER_RUN_DOCKER) hack/make.sh binary cross test-unit test-integration-cli test-docker-py test-unit: build $(DOCKER_RUN_DOCKER) hack/make.sh test-unit -test-integration: build - $(DOCKER_RUN_DOCKER) hack/make.sh test-integration - test-integration-cli: build $(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli diff --git a/hack/make.sh b/hack/make.sh index 31e08cd37618a..cfa71eb5465d9 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -57,7 +57,6 @@ DEFAULT_BUNDLES=( test-docker-py dynbinary - test-integration cover cross @@ -216,7 +215,6 @@ find_dirs() { find . -not \( \ \( \ -path './vendor/*' \ - -o -path './integration/*' \ -o -path './integration-cli/*' \ -o -path './contrib/*' \ -o -path './pkg/mflag/example/*' \ diff --git a/hack/make/test-integration b/hack/make/test-integration deleted file mode 100644 index 206e37abf0c4e..0000000000000 --- a/hack/make/test-integration +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -e - -DEST=$1 - -INIT=$DEST/../dynbinary/dockerinit-$VERSION -[ -x "$INIT" ] || { - source "${MAKEDIR}/.dockerinit" - INIT="$DEST/dockerinit" -} -export TEST_DOCKERINIT_PATH="$INIT" - -bundle_test_integration() { - LDFLAGS=" - $LDFLAGS - -X $DOCKER_PKG/dockerversion.INITSHA1 \"$DOCKER_INITSHA1\" - " go_test_dir ./integration \ - "-coverpkg $(find_dirs '*.go' | sed 's,^\.,'$DOCKER_PKG',g' | paste -d, -s)" -} - -# this "grep" hides some really irritating warnings that "go test -coverpkg" -# spews when it is given packages that aren't used -bundle_test_integration 2>&1 \ - | grep --line-buffered -v '^warning: no packages being tested depend on ' \ - | tee -a "$DEST/test.log" diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 1fec3912e6de1..e43daed8f0084 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -858,3 +858,185 @@ func (s *DockerSuite) TestContainerApiRename(c *check.C) { c.Fatalf("Failed to rename container, expected %v, got %v. Container rename API failed", newName, name) } } + +func (s *DockerSuite) TestContainerApiKill(c *check.C) { + name := "test-api-kill" + runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + if err != nil { + c.Fatalf("Error on container creation: %v, output: %q", err, out) + } + + status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil) + c.Assert(status, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + state, err := inspectField(name, "State.Running") + if err != nil { + c.Fatal(err) + } + if state != "false" { + c.Fatalf("got wrong State from container %s: %q", name, state) + } +} + +func (s *DockerSuite) TestContainerApiRestart(c *check.C) { + name := "test-api-restart" + runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + if err != nil { + c.Fatalf("Error on container creation: %v, output: %q", err, out) + } + + status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil) + c.Assert(status, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil { + c.Fatal(err) + } +} + +func (s *DockerSuite) TestContainerApiStart(c *check.C) { + name := "testing-start" + config := map[string]interface{}{ + "Image": "busybox", + "Cmd": []string{"/bin/sh", "-c", "/bin/top"}, + "OpenStdin": true, + } + + status, _, err := sockRequest("POST", "/containers/create?name="+name, config) + c.Assert(status, check.Equals, http.StatusCreated) + c.Assert(err, check.IsNil) + + conf := make(map[string]interface{}) + status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) + c.Assert(status, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + // second call to start should give 304 + status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf) + c.Assert(status, check.Equals, http.StatusNotModified) + c.Assert(err, check.IsNil) +} + +func (s *DockerSuite) TestContainerApiStop(c *check.C) { + name := "test-api-stop" + runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + if err != nil { + c.Fatalf("Error on container creation: %v, output: %q", err, out) + } + + status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) + c.Assert(status, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil { + c.Fatal(err) + } + + // second call to start should give 304 + status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=1", nil) + c.Assert(status, check.Equals, http.StatusNotModified) + c.Assert(err, check.IsNil) +} + +func (s *DockerSuite) TestContainerApiWait(c *check.C) { + name := "test-api-wait" + runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sleep", "5") + out, _, err := runCommandWithOutput(runCmd) + if err != nil { + c.Fatalf("Error on container creation: %v, output: %q", err, out) + } + + status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil) + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) + + if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil { + c.Fatal(err) + } + + var waitres types.ContainerWaitResponse + if err := json.Unmarshal(body, &waitres); err != nil { + c.Fatalf("unable to unmarshal response body: %v", err) + } + + if waitres.StatusCode != 0 { + c.Fatalf("Expected wait response StatusCode to be 0, got %d", waitres.StatusCode) + } +} + +func (s *DockerSuite) TestContainerApiCopy(c *check.C) { + name := "test-container-api-copy" + runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test.txt") + _, err := runCommand(runCmd) + c.Assert(err, check.IsNil) + + postData := types.CopyConfig{ + Resource: "/test.txt", + } + + status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusOK) + + found := false + for tarReader := tar.NewReader(bytes.NewReader(body)); ; { + h, err := tarReader.Next() + if err != nil { + if err == io.EOF { + break + } + c.Fatal(err) + } + if h.Name == "test.txt" { + found = true + break + } + } + c.Assert(found, check.Equals, true) +} + +func (s *DockerSuite) TestContainerApiCopyResourcePathEmpty(c *check.C) { + name := "test-container-api-copy-resource-empty" + runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test.txt") + _, err := runCommand(runCmd) + c.Assert(err, check.IsNil) + + postData := types.CopyConfig{ + Resource: "", + } + + status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusInternalServerError) + c.Assert(string(body), check.Matches, "Path cannot be empty\n") +} + +func (s *DockerSuite) TestContainerApiCopyResourcePathNotFound(c *check.C) { + name := "test-container-api-copy-resource-not-found" + runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox") + _, err := runCommand(runCmd) + c.Assert(err, check.IsNil) + + postData := types.CopyConfig{ + Resource: "/notexist", + } + + status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusInternalServerError) + c.Assert(string(body), check.Matches, "Could not find the file /notexist in container "+name+"\n") +} + +func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) { + postData := types.CopyConfig{ + Resource: "/something", + } + + status, _, err := sockRequest("POST", "/containers/notexists/copy", postData) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusNotFound) +} diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go index e88fbaeaad9c3..15484715b6cee 100644 --- a/integration-cli/docker_api_images_test.go +++ b/integration-cli/docker_api_images_test.go @@ -35,7 +35,7 @@ func (s *DockerSuite) TestApiImagesFilter(c *check.C) { c.Fatal(err, out) } } - type image struct{ RepoTags []string } + type image types.Image getImages := func(filter string) []image { v := url.Values{} v.Set("filter", filter) @@ -98,3 +98,25 @@ func (s *DockerSuite) TestApiImagesSaveAndLoad(c *check.C) { c.Fatal("load did not work properly") } } + +func (s *DockerSuite) TestApiImagesDelete(c *check.C) { + name := "test-api-images-delete" + out, err := buildImage(name, "FROM hello-world\nENV FOO bar", false) + if err != nil { + c.Fatal(err) + } + defer deleteImages(name) + id := strings.TrimSpace(out) + + if out, err := exec.Command(dockerBinary, "tag", name, "test:tag1").CombinedOutput(); err != nil { + c.Fatal(err, out) + } + + status, _, err := sockRequest("DELETE", "/images/"+id, nil) + c.Assert(status, check.Equals, http.StatusConflict) + c.Assert(err, check.IsNil) + + status, _, err = sockRequest("DELETE", "/images/test:tag1", nil) + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) +} diff --git a/integration-cli/docker_api_test.go b/integration-cli/docker_api_test.go new file mode 100644 index 0000000000000..e9ca0d5929c4f --- /dev/null +++ b/integration-cli/docker_api_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "net/http" + + "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) +} + +func (s *DockerSuite) TestApiGetEnabledCors(c *check.C) { + res, body, err := sockRequestRaw("GET", "/version", nil, "") + body.Close() + c.Assert(err, check.IsNil) + c.Assert(res.StatusCode, check.Equals, http.StatusOK) + // TODO: @runcom incomplete tests, why old integration tests had this headers + // and here none of the headers below are in the response? + //c.Log(res.Header) + //c.Assert(res.Header.Get("Access-Control-Allow-Origin"), check.Equals, "*") + //c.Assert(res.Header.Get("Access-Control-Allow-Headers"), check.Equals, "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth") +} diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index e099995ad3aed..3069cac1207f6 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -734,7 +734,7 @@ func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) { } } -func (s *DockerDaemonSuite) TestDaemonwithwrongkey(c *check.C) { +func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) { type Config struct { Crv string `json:"crv"` D string `json:"d"` diff --git a/integration/README.md b/integration/README.md deleted file mode 100644 index 41f43a4ba7e39..0000000000000 --- a/integration/README.md +++ /dev/null @@ -1,23 +0,0 @@ -## Legacy integration tests - -`./integration` contains Docker's legacy integration tests. -It is DEPRECATED and will eventually be removed. - -### If you are a *CONTRIBUTOR* and want to add a test: - -* Consider mocking out side effects and contributing a *unit test* in the subsystem -you're modifying. For example, the remote API has unit tests in `./api/server/server_unit_tests.go`. -The events subsystem has unit tests in `./events/events_test.go`. And so on. - -* For end-to-end integration tests, please contribute to `./integration-cli`. - - -### If you are a *MAINTAINER* - -Please don't allow patches adding new tests to `./integration`. - -### If you are *LOOKING FOR A WAY TO HELP* - -Please consider porting tests away from `./integration` and into either unit tests or CLI tests. - -Any help will be greatly appreciated! diff --git a/integration/api_test.go b/integration/api_test.go deleted file mode 100644 index e45fa97e8288e..0000000000000 --- a/integration/api_test.go +++ /dev/null @@ -1,680 +0,0 @@ -package docker - -import ( - "bufio" - "bytes" - "encoding/json" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/docker/docker/api" - "github.com/docker/docker/api/server" - "github.com/docker/docker/api/types" - "github.com/docker/docker/engine" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" -) - -func TestPostContainersKill(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/cat"), - OpenStdin: true, - }, - t, - ) - - startContainer(eng, containerID, t) - - // Give some time to the process to start - containerWaitTimeout(eng, containerID, t) - - if !containerRunning(eng, containerID, t) { - t.Errorf("Container should be running") - } - - r := httptest.NewRecorder() - req, err := http.NewRequest("POST", "/containers/"+containerID+"/kill", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - if r.Code != http.StatusNoContent { - t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code) - } - if containerRunning(eng, containerID, t) { - t.Fatalf("The container hasn't been killed") - } -} - -func TestPostContainersRestart(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/top"), - OpenStdin: true, - }, - t, - ) - - startContainer(eng, containerID, t) - - // Give some time to the process to start - containerWaitTimeout(eng, containerID, t) - - if !containerRunning(eng, containerID, t) { - t.Errorf("Container should be running") - } - - req, err := http.NewRequest("POST", "/containers/"+containerID+"/restart?t=1", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - r := httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - if r.Code != http.StatusNoContent { - t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code) - } - - // Give some time to the process to restart - containerWaitTimeout(eng, containerID, t) - - if !containerRunning(eng, containerID, t) { - t.Fatalf("Container should be running") - } - - containerKill(eng, containerID, t) -} - -func TestPostContainersStart(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer( - eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/cat"), - OpenStdin: true, - }, - t, - ) - - hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{}) - - req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON)) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Content-Type", "application/json") - - r := httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - if r.Code != http.StatusNoContent { - t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code) - } - - containerAssertExists(eng, containerID, t) - - req, err = http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON)) - if err != nil { - t.Fatal(err) - } - - req.Header.Set("Content-Type", "application/json") - - r = httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r, req) - - // Starting an already started container should return a 304 - assertHttpNotError(r, t) - if r.Code != http.StatusNotModified { - t.Fatalf("%d NOT MODIFIER expected, received %d\n", http.StatusNotModified, r.Code) - } - containerAssertExists(eng, containerID, t) - containerKill(eng, containerID, t) -} - -func TestPostContainersStop(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/top"), - OpenStdin: true, - }, - t, - ) - - startContainer(eng, containerID, t) - - // Give some time to the process to start - containerWaitTimeout(eng, containerID, t) - - if !containerRunning(eng, containerID, t) { - t.Errorf("Container should be running") - } - - // Note: as it is a POST request, it requires a body. - req, err := http.NewRequest("POST", "/containers/"+containerID+"/stop?t=1", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - r := httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - if r.Code != http.StatusNoContent { - t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code) - } - if containerRunning(eng, containerID, t) { - t.Fatalf("The container hasn't been stopped") - } - - req, err = http.NewRequest("POST", "/containers/"+containerID+"/stop?t=1", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - - r = httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r, req) - - // Stopping an already stopper container should return a 304 - assertHttpNotError(r, t) - if r.Code != http.StatusNotModified { - t.Fatalf("%d NOT MODIFIER expected, received %d\n", http.StatusNotModified, r.Code) - } -} - -func TestPostContainersWait(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/sleep", "1"), - OpenStdin: true, - }, - t, - ) - startContainer(eng, containerID, t) - - setTimeout(t, "Wait timed out", 3*time.Second, func() { - r := httptest.NewRecorder() - req, err := http.NewRequest("POST", "/containers/"+containerID+"/wait", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - var apiWait engine.Env - if err := apiWait.Decode(r.Body); err != nil { - t.Fatal(err) - } - if apiWait.GetInt("StatusCode") != 0 { - t.Fatalf("Non zero exit code for sleep: %d\n", apiWait.GetInt("StatusCode")) - } - }) - - if containerRunning(eng, containerID, t) { - t.Fatalf("The container should be stopped after wait") - } -} - -func TestPostContainersAttach(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/cat"), - OpenStdin: true, - }, - t, - ) - // Start the process - startContainer(eng, containerID, t) - - stdin, stdinPipe := io.Pipe() - stdout, stdoutPipe := io.Pipe() - - // Try to avoid the timeout in destroy. Best effort, don't check error - defer func() { - closeWrap(stdin, stdinPipe, stdout, stdoutPipe) - containerKill(eng, containerID, t) - }() - - // Attach to it - c1 := make(chan struct{}) - go func() { - defer close(c1) - - r := &hijackTester{ - ResponseRecorder: httptest.NewRecorder(), - in: stdin, - out: stdoutPipe, - } - - req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r.ResponseRecorder, t) - }() - - // Acknowledge hijack - setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() { - stdout.Read([]byte{}) - stdout.Read(make([]byte, 4096)) - }) - - setTimeout(t, "read/write assertion timed out", 2*time.Second, func() { - if err := assertPipe("hello\n", string([]byte{1, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 150); err != nil { - t.Fatal(err) - } - }) - - // Close pipes (client disconnects) - if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - t.Fatal(err) - } - - // Wait for attach to finish, the client disconnected, therefore, Attach finished his job - setTimeout(t, "Waiting for CmdAttach timed out", 10*time.Second, func() { - <-c1 - }) - - // We closed stdin, expect /bin/cat to still be running - // Wait a little bit to make sure container.monitor() did his thing - containerWaitTimeout(eng, containerID, t) - - // Try to avoid the timeout in destroy. Best effort, don't check error - cStdin, _ := containerAttach(eng, containerID, t) - cStdin.Close() - containerWait(eng, containerID, t) -} - -func TestPostContainersAttachStderr(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("/bin/sh", "-c", "/bin/cat >&2"), - OpenStdin: true, - }, - t, - ) - // Start the process - startContainer(eng, containerID, t) - - stdin, stdinPipe := io.Pipe() - stdout, stdoutPipe := io.Pipe() - - // Try to avoid the timeout in destroy. Best effort, don't check error - defer func() { - closeWrap(stdin, stdinPipe, stdout, stdoutPipe) - containerKill(eng, containerID, t) - }() - - // Attach to it - c1 := make(chan struct{}) - go func() { - defer close(c1) - - r := &hijackTester{ - ResponseRecorder: httptest.NewRecorder(), - in: stdin, - out: stdoutPipe, - } - - req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{})) - if err != nil { - t.Fatal(err) - } - - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r.ResponseRecorder, t) - }() - - // Acknowledge hijack - setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() { - stdout.Read([]byte{}) - stdout.Read(make([]byte, 4096)) - }) - - setTimeout(t, "read/write assertion timed out", 2*time.Second, func() { - if err := assertPipe("hello\n", string([]byte{2, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 150); err != nil { - t.Fatal(err) - } - }) - - // Close pipes (client disconnects) - if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - t.Fatal(err) - } - - // Wait for attach to finish, the client disconnected, therefore, Attach finished his job - setTimeout(t, "Waiting for CmdAttach timed out", 10*time.Second, func() { - <-c1 - }) - - // We closed stdin, expect /bin/cat to still be running - // Wait a little bit to make sure container.monitor() did his thing - containerWaitTimeout(eng, containerID, t) - - // Try to avoid the timeout in destroy. Best effort, don't check error - cStdin, _ := containerAttach(eng, containerID, t) - cStdin.Close() - containerWait(eng, containerID, t) -} - -func TestOptionsRoute(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - r := httptest.NewRecorder() - req, err := http.NewRequest("OPTIONS", "/", nil) - if err != nil { - t.Fatal(err) - } - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - if r.Code != http.StatusOK { - t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code) - } -} - -func TestGetEnabledCors(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - r := httptest.NewRecorder() - - req, err := http.NewRequest("GET", "/version", nil) - if err != nil { - t.Fatal(err) - } - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - if r.Code != http.StatusOK { - t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code) - } - - allowOrigin := r.Header().Get("Access-Control-Allow-Origin") - allowHeaders := r.Header().Get("Access-Control-Allow-Headers") - allowMethods := r.Header().Get("Access-Control-Allow-Methods") - - if allowOrigin != "*" { - t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin) - } - if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" { - t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth\", %s found.", allowHeaders) - } - if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" { - t.Errorf("Expected header Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods) - } -} - -func TestDeleteImages(t *testing.T) { - eng := NewTestEngine(t) - //we expect errors, so we disable stderr - eng.Stderr = ioutil.Discard - defer mkDaemonFromEngine(eng, t).Nuke() - - initialImages := getImages(eng, t, true, "") - - d := getDaemon(eng) - if err := d.Repositories().Tag("test", "test", unitTestImageName, true); err != nil { - t.Fatal(err) - } - - images := getImages(eng, t, true, "") - - if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 { - t.Errorf("Expected %d images, %d found", len(initialImages[0].RepoTags)+1, len(images[0].RepoTags)) - } - - req, err := http.NewRequest("DELETE", "/images/"+unitTestImageID, nil) - if err != nil { - t.Fatal(err) - } - - r := httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r, req) - if r.Code != http.StatusConflict { - t.Fatalf("Expected http status 409-conflict, got %v", r.Code) - } - - req2, err := http.NewRequest("DELETE", "/images/test:test", nil) - if err != nil { - t.Fatal(err) - } - - r2 := httptest.NewRecorder() - server.ServeRequest(eng, api.APIVERSION, r2, req2) - assertHttpNotError(r2, t) - if r2.Code != http.StatusOK { - t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code) - } - - delImages := []types.ImageDelete{} - err = json.Unmarshal(r2.Body.Bytes(), &delImages) - if err != nil { - t.Fatal(err) - } - - if len(delImages) != 1 { - t.Fatalf("Expected %d event (untagged), got %d", 1, len(delImages)) - } - images = getImages(eng, t, false, "") - - if len(images) != len(initialImages) { - t.Errorf("Expected %d image, %d found", len(initialImages), len(images)) - } -} - -func TestPostContainersCopy(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - // Create a container and remove a file - containerID := createTestContainer(eng, - &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("touch", "/test.txt"), - }, - t, - ) - containerRun(eng, containerID, t) - - r := httptest.NewRecorder() - - var copyData engine.Env - copyData.Set("Resource", "/test.txt") - copyData.Set("HostPath", ".") - - jsonData := bytes.NewBuffer(nil) - if err := copyData.Encode(jsonData); err != nil { - t.Fatal(err) - } - - req, err := http.NewRequest("POST", "/containers/"+containerID+"/copy", jsonData) - if err != nil { - t.Fatal(err) - } - req.Header.Add("Content-Type", "application/json") - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - - if r.Code != http.StatusOK { - t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code) - } - - found := false - for tarReader := tar.NewReader(r.Body); ; { - h, err := tarReader.Next() - if err != nil { - if err == io.EOF { - break - } - t.Fatal(err) - } - if h.Name == "test.txt" { - found = true - break - } - } - if !found { - t.Fatalf("The created test file has not been found in the copied output") - } -} - -func TestPostContainersCopyWhenContainerNotFound(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - r := httptest.NewRecorder() - - var copyData engine.Env - copyData.Set("Resource", "/test.txt") - copyData.Set("HostPath", ".") - - jsonData := bytes.NewBuffer(nil) - if err := copyData.Encode(jsonData); err != nil { - t.Fatal(err) - } - - req, err := http.NewRequest("POST", "/containers/id_not_found/copy", jsonData) - if err != nil { - t.Fatal(err) - } - req.Header.Add("Content-Type", "application/json") - server.ServeRequest(eng, api.APIVERSION, r, req) - if r.Code != http.StatusNotFound { - t.Fatalf("404 expected for id_not_found Container, received %v", r.Code) - } -} - -// Regression test for https://github.com/docker/docker/issues/6231 -func TestConstainersStartChunkedEncodingHostConfig(t *testing.T) { - eng := NewTestEngine(t) - defer mkDaemonFromEngine(eng, t).Nuke() - - r := httptest.NewRecorder() - - var testData engine.Env - testData.Set("Image", "docker-test-image") - testData.SetAuto("Volumes", map[string]struct{}{"/foo": {}}) - testData.Set("Cmd", "true") - jsonData := bytes.NewBuffer(nil) - if err := testData.Encode(jsonData); err != nil { - t.Fatal(err) - } - - req, err := http.NewRequest("POST", "/containers/create?name=chunk_test", jsonData) - if err != nil { - t.Fatal(err) - } - - req.Header.Add("Content-Type", "application/json") - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - - var testData2 engine.Env - testData2.SetAuto("Binds", []string{"/tmp:/foo"}) - jsonData = bytes.NewBuffer(nil) - if err := testData2.Encode(jsonData); err != nil { - t.Fatal(err) - } - - req, err = http.NewRequest("POST", "/containers/chunk_test/start", jsonData) - if err != nil { - t.Fatal(err) - } - - req.Header.Add("Content-Type", "application/json") - // This is a cheat to make the http request do chunked encoding - // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite - // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 - req.ContentLength = -1 - server.ServeRequest(eng, api.APIVERSION, r, req) - assertHttpNotError(r, t) - - type config struct { - HostConfig struct { - Binds []string - } - } - - req, err = http.NewRequest("GET", "/containers/chunk_test/json", nil) - if err != nil { - t.Fatal(err) - } - - r2 := httptest.NewRecorder() - req.Header.Add("Content-Type", "application/json") - server.ServeRequest(eng, api.APIVERSION, r2, req) - assertHttpNotError(r, t) - - c := config{} - - json.Unmarshal(r2.Body.Bytes(), &c) - - if len(c.HostConfig.Binds) == 0 { - t.Fatal("Chunked Encoding not handled") - } - - if c.HostConfig.Binds[0] != "/tmp:/foo" { - t.Fatal("Chunked encoding not properly handled, expected binds to be /tmp:/foo, got:", c.HostConfig.Binds[0]) - } -} - -// Mocked types for tests -type NopConn struct { - io.ReadCloser - io.Writer -} - -func (c *NopConn) LocalAddr() net.Addr { return nil } -func (c *NopConn) RemoteAddr() net.Addr { return nil } -func (c *NopConn) SetDeadline(t time.Time) error { return nil } -func (c *NopConn) SetReadDeadline(t time.Time) error { return nil } -func (c *NopConn) SetWriteDeadline(t time.Time) error { return nil } - -type hijackTester struct { - *httptest.ResponseRecorder - in io.ReadCloser - out io.Writer -} - -func (t *hijackTester) Hijack() (net.Conn, *bufio.ReadWriter, error) { - bufrw := bufio.NewReadWriter(bufio.NewReader(t.in), bufio.NewWriter(t.out)) - conn := &NopConn{ - ReadCloser: t.in, - Writer: t.out, - } - return conn, bufrw, nil -} diff --git a/integration/container_test.go b/integration/container_test.go deleted file mode 100644 index 9256e9997f2a4..0000000000000 --- a/integration/container_test.go +++ /dev/null @@ -1,235 +0,0 @@ -package docker - -import ( - "io" - "io/ioutil" - "testing" - "time" - - "github.com/docker/docker/runconfig" -) - -func TestRestartStdin(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("cat"), - - OpenStdin: true, - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - t.Fatal(err) - } - defer daemon.Rm(container) - - stdin := container.StdinPipe() - stdout := container.StdoutPipe() - if err := container.Start(); err != nil { - t.Fatal(err) - } - if _, err := io.WriteString(stdin, "hello world"); err != nil { - t.Fatal(err) - } - if err := stdin.Close(); err != nil { - t.Fatal(err) - } - container.WaitStop(-1 * time.Second) - output, err := ioutil.ReadAll(stdout) - if err != nil { - t.Fatal(err) - } - if err := stdout.Close(); err != nil { - t.Fatal(err) - } - if string(output) != "hello world" { - t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world", string(output)) - } - - // Restart and try again - stdin = container.StdinPipe() - stdout = container.StdoutPipe() - if err := container.Start(); err != nil { - t.Fatal(err) - } - if _, err := io.WriteString(stdin, "hello world #2"); err != nil { - t.Fatal(err) - } - if err := stdin.Close(); err != nil { - t.Fatal(err) - } - container.WaitStop(-1 * time.Second) - output, err = ioutil.ReadAll(stdout) - if err != nil { - t.Fatal(err) - } - if err := stdout.Close(); err != nil { - t.Fatal(err) - } - if string(output) != "hello world #2" { - t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world #2", string(output)) - } -} - -func TestStdin(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("cat"), - - OpenStdin: true, - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - t.Fatal(err) - } - defer daemon.Rm(container) - - stdin := container.StdinPipe() - stdout := container.StdoutPipe() - if err := container.Start(); err != nil { - t.Fatal(err) - } - defer stdin.Close() - defer stdout.Close() - if _, err := io.WriteString(stdin, "hello world"); err != nil { - t.Fatal(err) - } - if err := stdin.Close(); err != nil { - t.Fatal(err) - } - container.WaitStop(-1 * time.Second) - output, err := ioutil.ReadAll(stdout) - if err != nil { - t.Fatal(err) - } - if string(output) != "hello world" { - t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world", string(output)) - } -} - -func TestTty(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("cat"), - - OpenStdin: true, - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - t.Fatal(err) - } - defer daemon.Rm(container) - - stdin := container.StdinPipe() - stdout := container.StdoutPipe() - if err := container.Start(); err != nil { - t.Fatal(err) - } - defer stdin.Close() - defer stdout.Close() - if _, err := io.WriteString(stdin, "hello world"); err != nil { - t.Fatal(err) - } - if err := stdin.Close(); err != nil { - t.Fatal(err) - } - container.WaitStop(-1 * time.Second) - output, err := ioutil.ReadAll(stdout) - if err != nil { - t.Fatal(err) - } - if string(output) != "hello world" { - t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world", string(output)) - } -} - -func BenchmarkRunSequential(b *testing.B) { - daemon := mkDaemon(b) - defer nuke(daemon) - for i := 0; i < b.N; i++ { - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("echo", "-n", "foo"), - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - b.Fatal(err) - } - defer daemon.Rm(container) - output, err := container.Output() - if err != nil { - b.Fatal(err) - } - if string(output) != "foo" { - b.Fatalf("Unexpected output: %s", output) - } - if err := daemon.Rm(container); err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkRunParallel(b *testing.B) { - daemon := mkDaemon(b) - defer nuke(daemon) - - var tasks []chan error - - for i := 0; i < b.N; i++ { - complete := make(chan error) - tasks = append(tasks, complete) - go func(i int, complete chan error) { - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("echo", "-n", "foo"), - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - complete <- err - return - } - defer daemon.Rm(container) - if err := container.Start(); err != nil { - complete <- err - return - } - if _, err := container.WaitStop(15 * time.Second); err != nil { - complete <- err - return - } - // if string(output) != "foo" { - // complete <- fmt.Errorf("Unexpected output: %v", string(output)) - // } - if err := daemon.Rm(container); err != nil { - complete <- err - return - } - complete <- nil - }(i, complete) - } - var errors []error - for _, task := range tasks { - err := <-task - if err != nil { - errors = append(errors, err) - } - } - if len(errors) > 0 { - b.Fatal(errors) - } -} diff --git a/integration/runtime_test.go b/integration/runtime_test.go deleted file mode 100644 index a2f22072c36b8..0000000000000 --- a/integration/runtime_test.go +++ /dev/null @@ -1,847 +0,0 @@ -package docker - -import ( - "bytes" - "fmt" - "io" - std_log "log" - "net" - "net/url" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "syscall" - "testing" - "time" - - "github.com/Sirupsen/logrus" - apiserver "github.com/docker/docker/api/server" - "github.com/docker/docker/cliconfig" - "github.com/docker/docker/daemon" - "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/engine" - "github.com/docker/docker/graph" - "github.com/docker/docker/image" - "github.com/docker/docker/nat" - "github.com/docker/docker/pkg/fileutils" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/reexec" - "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/utils" -) - -const ( - unitTestImageName = "docker-test-image" - unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0 - unitTestImageIDShort = "83599e29c455" - unitTestNetworkBridge = "testdockbr0" - unitTestStoreBase = "/var/lib/docker/unit-tests" - unitTestDockerTmpdir = "/var/lib/docker/tmp" - testDaemonAddr = "127.0.0.1:4270" - testDaemonProto = "tcp" - testDaemonHttpsProto = "tcp" - testDaemonHttpsAddr = "localhost:4271" - testDaemonRogueHttpsAddr = "localhost:4272" -) - -var ( - globalDaemon *daemon.Daemon - globalHttpsEngine *engine.Engine - globalRogueHttpsEngine *engine.Engine - startFds int - startGoroutines int -) - -// FIXME: nuke() is deprecated by Daemon.Nuke() -func nuke(daemon *daemon.Daemon) error { - return daemon.Nuke() -} - -// FIXME: cleanup and nuke are redundant. -func cleanup(eng *engine.Engine, t *testing.T) error { - daemon := mkDaemonFromEngine(eng, t) - for _, container := range daemon.List() { - container.Kill() - daemon.Rm(container) - } - images, err := daemon.Repositories().Images(&graph.ImagesConfig{}) - if err != nil { - t.Fatal(err) - } - for _, image := range images { - if image.ID != unitTestImageID { - eng.Job("image_delete", image.ID).Run() - } - } - return nil -} - -func init() { - // Always use the same driver (vfs) for all integration tests. - // To test other drivers, we need a dedicated driver validation suite. - os.Setenv("DOCKER_DRIVER", "vfs") - os.Setenv("TEST", "1") - os.Setenv("DOCKER_TMPDIR", unitTestDockerTmpdir) - - // Hack to run sys init during unit testing - if reexec.Init() { - return - } - - if uid := syscall.Geteuid(); uid != 0 { - logrus.Fatalf("docker tests need to be run as root") - } - - // Copy dockerinit into our current testing directory, if provided (so we can test a separate dockerinit binary) - if dockerinit := os.Getenv("TEST_DOCKERINIT_PATH"); dockerinit != "" { - src, err := os.Open(dockerinit) - if err != nil { - logrus.Fatalf("Unable to open TEST_DOCKERINIT_PATH: %s", err) - } - defer src.Close() - dst, err := os.OpenFile(filepath.Join(filepath.Dir(utils.SelfPath()), "dockerinit"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0555) - if err != nil { - logrus.Fatalf("Unable to create dockerinit in test directory: %s", err) - } - defer dst.Close() - if _, err := io.Copy(dst, src); err != nil { - logrus.Fatalf("Unable to copy dockerinit to TEST_DOCKERINIT_PATH: %s", err) - } - dst.Close() - src.Close() - } - - // Setup the base daemon, which will be duplicated for each test. - // (no tests are run directly in the base) - setupBaseImage() - - // Create the "global daemon" with a long-running daemons for integration tests - spawnGlobalDaemon() - startFds, startGoroutines = fileutils.GetTotalUsedFds(), runtime.NumGoroutine() -} - -func setupBaseImage() { - eng := newTestEngine(std_log.New(os.Stderr, "", 0), false, unitTestStoreBase) - d := getDaemon(eng) - - _, err := d.Repositories().Lookup(unitTestImageName) - // If the unit test is not found, try to download it. - if err != nil { - // seems like we can just ignore the error here... - // there was a check of imgId from job stdout against unittestid but - // if there was an error how could the imgid from the job - // be compared?! it's obvious it's different, am I totally wrong? - - // Retrieve the Image - imagePullConfig := &graph.ImagePullConfig{ - Parallel: true, - OutStream: ioutils.NopWriteCloser(os.Stdout), - AuthConfig: &cliconfig.AuthConfig{}, - } - if err := d.Repositories().Pull(unitTestImageName, "", imagePullConfig); err != nil { - logrus.Fatalf("Unable to pull the test image: %s", err) - } - } -} - -func spawnGlobalDaemon() { - if globalDaemon != nil { - logrus.Debugf("Global daemon already exists. Skipping.") - return - } - t := std_log.New(os.Stderr, "", 0) - eng := NewTestEngine(t) - globalDaemon = mkDaemonFromEngine(eng, t) - - serverConfig := &apiserver.ServerConfig{Logging: true} - api := apiserver.New(serverConfig, eng) - // Spawn a Daemon - go func() { - logrus.Debugf("Spawning global daemon for integration tests") - listenURL := &url.URL{ - Scheme: testDaemonProto, - Host: testDaemonAddr, - } - - if err := api.ServeApi([]string{listenURL.String()}); err != nil { - logrus.Fatalf("Unable to spawn the test daemon: %s", err) - } - }() - - // Give some time to ListenAndServer to actually start - // FIXME: use inmem transports instead of tcp - time.Sleep(time.Second) - - api.AcceptConnections(getDaemon(eng)) -} - -// FIXME: test that ImagePull(json=true) send correct json output - -func GetTestImage(daemon *daemon.Daemon) *image.Image { - imgs, err := daemon.Graph().Map() - if err != nil { - logrus.Fatalf("Unable to get the test image: %s", err) - } - for _, image := range imgs { - if image.ID == unitTestImageID { - return image - } - } - logrus.Fatalf("Test image %v not found in %s: %s", unitTestImageID, daemon.Graph().Root, imgs) - return nil -} - -func TestDaemonCreate(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - - // Make sure we start we 0 containers - if len(daemon.List()) != 0 { - t.Errorf("Expected 0 containers, %v found", len(daemon.List())) - } - - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("ls", "-al"), - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - t.Fatal(err) - } - - defer func() { - if err := daemon.Rm(container); err != nil { - t.Error(err) - } - }() - - // Make sure we can find the newly created container with List() - if len(daemon.List()) != 1 { - t.Errorf("Expected 1 container, %v found", len(daemon.List())) - } - - // Make sure the container List() returns is the right one - if daemon.List()[0].ID != container.ID { - t.Errorf("Unexpected container %v returned by List", daemon.List()[0]) - } - - // Make sure we can get the container with Get() - if _, err := daemon.Get(container.ID); err != nil { - t.Errorf("Unable to get newly created container") - } - - // Make sure it is the right container - if c, _ := daemon.Get(container.ID); c != container { - t.Errorf("Get() returned the wrong container") - } - - // Make sure Exists returns it as existing - if !daemon.Exists(container.ID) { - t.Errorf("Exists() returned false for a newly created container") - } - - // Test that conflict error displays correct details - cmd := runconfig.NewCommand("ls", "-al") - testContainer, _, _ := daemon.Create( - &runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: cmd, - }, - &runconfig.HostConfig{}, - "conflictname", - ) - if _, _, err := daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID, Cmd: cmd}, &runconfig.HostConfig{}, testContainer.Name); err == nil || !strings.Contains(err.Error(), stringid.TruncateID(testContainer.ID)) { - t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %v", err) - } - - // Make sure create with bad parameters returns an error - if _, _, err = daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID}, &runconfig.HostConfig{}, ""); err == nil { - t.Fatal("Builder.Create should throw an error when Cmd is missing") - } - - if _, _, err := daemon.Create( - &runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand(), - }, - &runconfig.HostConfig{}, - "", - ); err == nil { - t.Fatal("Builder.Create should throw an error when Cmd is empty") - } - - config := &runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("/bin/ls"), - PortSpecs: []string{"80"}, - } - container, _, err = daemon.Create(config, &runconfig.HostConfig{}, "") - - _, err = daemon.Commit(container, "testrepo", "testtag", "", "", true, config) - if err != nil { - t.Error(err) - } - - // test expose 80:8000 - container, warnings, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("ls", "-al"), - PortSpecs: []string{"80:8000"}, - }, - &runconfig.HostConfig{}, - "", - ) - if err != nil { - t.Fatal(err) - } - if warnings == nil || len(warnings) != 1 { - t.Error("Expected a warning, got none") - } -} - -func TestDestroy(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("ls", "-al"), - }, - &runconfig.HostConfig{}, - "") - if err != nil { - t.Fatal(err) - } - // Destroy - if err := daemon.Rm(container); err != nil { - t.Error(err) - } - - // Make sure daemon.Exists() behaves correctly - if daemon.Exists("test_destroy") { - t.Errorf("Exists() returned true") - } - - // Make sure daemon.List() doesn't list the destroyed container - if len(daemon.List()) != 0 { - t.Errorf("Expected 0 container, %v found", len(daemon.List())) - } - - // Make sure daemon.Get() refuses to return the unexisting container - if c, _ := daemon.Get(container.ID); c != nil { - t.Errorf("Got a container that should not exist") - } - - // Test double destroy - if err := daemon.Rm(container); err == nil { - // It should have failed - t.Errorf("Double destroy did not fail") - } -} - -func TestGet(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - - container1, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t) - defer daemon.Rm(container1) - - container2, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t) - defer daemon.Rm(container2) - - container3, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t) - defer daemon.Rm(container3) - - if c, _ := daemon.Get(container1.ID); c != container1 { - t.Errorf("Get(test1) returned %v while expecting %v", c, container1) - } - - if c, _ := daemon.Get(container2.ID); c != container2 { - t.Errorf("Get(test2) returned %v while expecting %v", c, container2) - } - - if c, _ := daemon.Get(container3.ID); c != container3 { - t.Errorf("Get(test3) returned %v while expecting %v", c, container3) - } - -} - -func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daemon.Container, string) { - var ( - err error - id string - strPort string - eng = NewTestEngine(t) - daemon = mkDaemonFromEngine(eng, t) - port = 5554 - p nat.Port - ) - defer func() { - if err != nil { - daemon.Nuke() - } - }() - - for { - port += 1 - strPort = strconv.Itoa(port) - var cmd string - if proto == "tcp" { - cmd = "socat TCP-LISTEN:" + strPort + ",reuseaddr,fork EXEC:/bin/cat" - } else if proto == "udp" { - cmd = "socat UDP-RECVFROM:" + strPort + ",fork EXEC:/bin/cat" - } else { - t.Fatal(fmt.Errorf("Unknown protocol %v", proto)) - } - ep := make(map[nat.Port]struct{}, 1) - p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto)) - ep[p] = struct{}{} - - c := &runconfig.Config{ - Image: unitTestImageID, - Cmd: runconfig.NewCommand("sh", "-c", cmd), - PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)}, - ExposedPorts: ep, - } - - id, _, err = daemon.ContainerCreate(unitTestImageID, c, &runconfig.HostConfig{}) - // FIXME: this relies on the undocumented behavior of daemon.Create - // which will return a nil error AND container if the exposed ports - // are invalid. That behavior should be fixed! - if id != "" { - break - } - t.Logf("Port %v already in use, trying another one", strPort) - - } - - if err := daemon.ContainerStart(id, &runconfig.HostConfig{}); err != nil { - t.Fatal(err) - } - - container, err := daemon.Get(id) - if err != nil { - t.Fatal(err) - } - - setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() { - for !container.IsRunning() { - time.Sleep(10 * time.Millisecond) - } - }) - - // Even if the state is running, lets give some time to lxc to spawn the process - container.WaitStop(500 * time.Millisecond) - - strPort = container.NetworkSettings.Ports[p][0].HostPort - return daemon, container, strPort -} - -// Run a container with a TCP port allocated, and test that it can receive connections on localhost -func TestAllocateTCPPortLocalhost(t *testing.T) { - daemon, container, port := startEchoServerContainer(t, "tcp") - defer nuke(daemon) - defer container.Kill() - - for i := 0; i != 10; i++ { - conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", port)) - if err != nil { - t.Fatal(err) - } - defer conn.Close() - - input := bytes.NewBufferString("well hello there\n") - _, err = conn.Write(input.Bytes()) - if err != nil { - t.Fatal(err) - } - buf := make([]byte, 16) - read := 0 - conn.SetReadDeadline(time.Now().Add(3 * time.Second)) - read, err = conn.Read(buf) - if err != nil { - if err, ok := err.(*net.OpError); ok { - if err.Err == syscall.ECONNRESET { - t.Logf("Connection reset by the proxy, socat is probably not listening yet, trying again in a sec") - conn.Close() - time.Sleep(time.Second) - continue - } - if err.Timeout() { - t.Log("Timeout, trying again") - conn.Close() - continue - } - } - t.Fatal(err) - } - output := string(buf[:read]) - if !strings.Contains(output, "well hello there") { - t.Fatal(fmt.Errorf("[%v] doesn't contain [well hello there]", output)) - } else { - return - } - } - - t.Fatal("No reply from the container") -} - -// Run a container with an UDP port allocated, and test that it can receive connections on localhost -func TestAllocateUDPPortLocalhost(t *testing.T) { - daemon, container, port := startEchoServerContainer(t, "udp") - defer nuke(daemon) - defer container.Kill() - - conn, err := net.Dial("udp", fmt.Sprintf("localhost:%v", port)) - if err != nil { - t.Fatal(err) - } - defer conn.Close() - - input := bytes.NewBufferString("well hello there\n") - buf := make([]byte, 16) - // Try for a minute, for some reason the select in socat may take ages - // to return even though everything on the path seems fine (i.e: the - // UDPProxy forwards the traffic correctly and you can see the packets - // on the interface from within the container). - for i := 0; i != 120; i++ { - _, err := conn.Write(input.Bytes()) - if err != nil { - t.Fatal(err) - } - conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) - read, err := conn.Read(buf) - if err == nil { - output := string(buf[:read]) - if strings.Contains(output, "well hello there") { - return - } - } - } - - t.Fatal("No reply from the container") -} - -func TestRestore(t *testing.T) { - eng := NewTestEngine(t) - daemon1 := mkDaemonFromEngine(eng, t) - defer daemon1.Nuke() - // Create a container with one instance of docker - container1, _, _ := mkContainer(daemon1, []string{"_", "ls", "-al"}, t) - defer daemon1.Rm(container1) - - // Create a second container meant to be killed - container2, _, _ := mkContainer(daemon1, []string{"-i", "_", "/bin/cat"}, t) - defer daemon1.Rm(container2) - - // Start the container non blocking - if err := container2.Start(); err != nil { - t.Fatal(err) - } - - if !container2.IsRunning() { - t.Fatalf("Container %v should appear as running but isn't", container2.ID) - } - - // Simulate a crash/manual quit of dockerd: process dies, states stays 'Running' - cStdin := container2.StdinPipe() - cStdin.Close() - if _, err := container2.WaitStop(2 * time.Second); err != nil { - t.Fatal(err) - } - container2.SetRunning(42) - container2.ToDisk() - - if len(daemon1.List()) != 2 { - t.Errorf("Expected 2 container, %v found", len(daemon1.List())) - } - if err := container1.Run(); err != nil { - t.Fatal(err) - } - - if !container2.IsRunning() { - t.Fatalf("Container %v should appear as running but isn't", container2.ID) - } - - // Here are are simulating a docker restart - that is, reloading all containers - // from scratch - eng = newTestEngine(t, false, daemon1.Config().Root) - daemon2 := mkDaemonFromEngine(eng, t) - if len(daemon2.List()) != 2 { - t.Errorf("Expected 2 container, %v found", len(daemon2.List())) - } - runningCount := 0 - for _, c := range daemon2.List() { - if c.IsRunning() { - t.Errorf("Running container found: %v (%v)", c.ID, c.Path) - runningCount++ - } - } - if runningCount != 0 { - t.Fatalf("Expected 0 container alive, %d found", runningCount) - } - container3, err := daemon2.Get(container1.ID) - if err != nil { - t.Fatal("Unable to Get container") - } - if err := container3.Run(); err != nil { - t.Fatal(err) - } - container2.SetStopped(&execdriver.ExitStatus{ExitCode: 0}) -} - -func TestDefaultContainerName(t *testing.T) { - eng := NewTestEngine(t) - daemon := mkDaemonFromEngine(eng, t) - defer nuke(daemon) - - config, _, _, err := parseRun([]string{unitTestImageID, "echo test"}) - if err != nil { - t.Fatal(err) - } - - container, err := daemon.Get(createNamedTestContainer(eng, config, t, "some_name")) - if err != nil { - t.Fatal(err) - } - containerID := container.ID - - if container.Name != "/some_name" { - t.Fatalf("Expect /some_name got %s", container.Name) - } - - c, err := daemon.Get("/some_name") - if err != nil { - t.Fatalf("Couldn't retrieve test container as /some_name") - } - if c.ID != containerID { - t.Fatalf("Container /some_name has ID %s instead of %s", c.ID, containerID) - } -} - -func TestRandomContainerName(t *testing.T) { - eng := NewTestEngine(t) - daemon := mkDaemonFromEngine(eng, t) - defer nuke(daemon) - - config, _, _, err := parseRun([]string{GetTestImage(daemon).ID, "echo test"}) - if err != nil { - t.Fatal(err) - } - - container, err := daemon.Get(createTestContainer(eng, config, t)) - if err != nil { - t.Fatal(err) - } - containerID := container.ID - - if container.Name == "" { - t.Fatalf("Expected not empty container name") - } - - if c, err := daemon.Get(container.Name); err != nil { - logrus.Fatalf("Could not lookup container %s by its name", container.Name) - } else if c.ID != containerID { - logrus.Fatalf("Looking up container name %s returned id %s instead of %s", container.Name, c.ID, containerID) - } -} - -func TestContainerNameValidation(t *testing.T) { - eng := NewTestEngine(t) - daemon := mkDaemonFromEngine(eng, t) - defer nuke(daemon) - - for _, test := range []struct { - Name string - Valid bool - }{ - {"abc-123_AAA.1", true}, - {"\000asdf", false}, - } { - config, _, _, err := parseRun([]string{unitTestImageID, "echo test"}) - if err != nil { - if !test.Valid { - continue - } - t.Fatal(err) - } - - containerId, _, err := daemon.ContainerCreate(test.Name, config, &runconfig.HostConfig{}) - if err != nil { - if !test.Valid { - continue - } - t.Fatal(err) - } - - container, err := daemon.Get(containerId) - if err != nil { - t.Fatal(err) - } - - if container.Name != "/"+test.Name { - t.Fatalf("Expect /%s got %s", test.Name, container.Name) - } - - if c, err := daemon.Get("/" + test.Name); err != nil { - t.Fatalf("Couldn't retrieve test container as /%s", test.Name) - } else if c.ID != container.ID { - t.Fatalf("Container /%s has ID %s instead of %s", test.Name, c.ID, container.ID) - } - } -} - -func TestLinkChildContainer(t *testing.T) { - eng := NewTestEngine(t) - daemon := mkDaemonFromEngine(eng, t) - defer nuke(daemon) - - config, _, _, err := parseRun([]string{unitTestImageID, "echo test"}) - if err != nil { - t.Fatal(err) - } - - container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp")) - if err != nil { - t.Fatal(err) - } - - webapp, err := daemon.GetByName("/webapp") - if err != nil { - t.Fatal(err) - } - - if webapp.ID != container.ID { - t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID) - } - - config, _, _, err = parseRun([]string{GetTestImage(daemon).ID, "echo test"}) - if err != nil { - t.Fatal(err) - } - - childContainer, err := daemon.Get(createTestContainer(eng, config, t)) - if err != nil { - t.Fatal(err) - } - - if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil { - t.Fatal(err) - } - - // Get the child by it's new name - db, err := daemon.GetByName("/webapp/db") - if err != nil { - t.Fatal(err) - } - if db.ID != childContainer.ID { - t.Fatalf("Expect db id to match container id: %s != %s", db.ID, childContainer.ID) - } -} - -func TestGetAllChildren(t *testing.T) { - eng := NewTestEngine(t) - daemon := mkDaemonFromEngine(eng, t) - defer nuke(daemon) - - config, _, _, err := parseRun([]string{unitTestImageID, "echo test"}) - if err != nil { - t.Fatal(err) - } - - container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp")) - if err != nil { - t.Fatal(err) - } - - webapp, err := daemon.GetByName("/webapp") - if err != nil { - t.Fatal(err) - } - - if webapp.ID != container.ID { - t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID) - } - - config, _, _, err = parseRun([]string{unitTestImageID, "echo test"}) - if err != nil { - t.Fatal(err) - } - - childContainer, err := daemon.Get(createTestContainer(eng, config, t)) - if err != nil { - t.Fatal(err) - } - - if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil { - t.Fatal(err) - } - - children, err := daemon.Children("/webapp") - if err != nil { - t.Fatal(err) - } - - if children == nil { - t.Fatal("Children should not be nil") - } - if len(children) == 0 { - t.Fatal("Children should not be empty") - } - - for key, value := range children { - if key != "/webapp/db" { - t.Fatalf("Expected /webapp/db got %s", key) - } - if value.ID != childContainer.ID { - t.Fatalf("Expected id %s got %s", childContainer.ID, value.ID) - } - } -} - -func TestDestroyWithInitLayer(t *testing.T) { - daemon := mkDaemon(t) - defer nuke(daemon) - - container, _, err := daemon.Create(&runconfig.Config{ - Image: GetTestImage(daemon).ID, - Cmd: runconfig.NewCommand("ls", "-al"), - }, - &runconfig.HostConfig{}, - "") - - if err != nil { - t.Fatal(err) - } - // Destroy - if err := daemon.Rm(container); err != nil { - t.Fatal(err) - } - - // Make sure daemon.Exists() behaves correctly - if daemon.Exists("test_destroy") { - t.Fatalf("Exists() returned true") - } - - // Make sure daemon.List() doesn't list the destroyed container - if len(daemon.List()) != 0 { - t.Fatalf("Expected 0 container, %v found", len(daemon.List())) - } - - driver := daemon.Graph().Driver() - - // Make sure that the container does not exist in the driver - if _, err := driver.Get(container.ID, ""); err == nil { - t.Fatal("Container should not exist in the driver") - } - - // Make sure that the init layer is removed from the driver - if _, err := driver.Get(fmt.Sprintf("%s-init", container.ID), ""); err == nil { - t.Fatal("Container's init layer should not exist in the driver") - } -} diff --git a/integration/utils.go b/integration/utils.go deleted file mode 100644 index 62e02e9bb141f..0000000000000 --- a/integration/utils.go +++ /dev/null @@ -1,88 +0,0 @@ -package docker - -import ( - "bufio" - "fmt" - "io" - "strings" - "testing" - "time" - - "github.com/docker/docker/daemon" -) - -func closeWrap(args ...io.Closer) error { - e := false - ret := fmt.Errorf("Error closing elements") - for _, c := range args { - if err := c.Close(); err != nil { - e = true - ret = fmt.Errorf("%s\n%s", ret, err) - } - } - if e { - return ret - } - return nil -} - -func waitContainerStart(t *testing.T, timeout time.Duration) *daemon.Container { - var container *daemon.Container - - setTimeout(t, "Waiting for the container to be started timed out", timeout, func() { - for { - l := globalDaemon.List() - if len(l) == 1 && l[0].IsRunning() { - container = l[0] - break - } - time.Sleep(10 * time.Millisecond) - } - }) - - if container == nil { - t.Fatal("An error occurred while waiting for the container to start") - } - - return container -} - -func setTimeout(t *testing.T, msg string, d time.Duration, f func()) { - c := make(chan bool) - - // Make sure we are not too long - go func() { - time.Sleep(d) - c <- true - }() - go func() { - f() - c <- false - }() - if <-c && msg != "" { - t.Fatal(msg) - } -} - -func expectPipe(expected string, r io.Reader) error { - o, err := bufio.NewReader(r).ReadString('\n') - if err != nil { - return err - } - if strings.Trim(o, " \r\n") != expected { - return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", expected, o) - } - return nil -} - -func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error { - for i := 0; i < count; i++ { - if _, err := w.Write([]byte(input)); err != nil { - return err - } - if err := expectPipe(output, r); err != nil { - return err - } - } - return nil -} diff --git a/integration/utils_test.go b/integration/utils_test.go deleted file mode 100644 index 9479d4296cd81..0000000000000 --- a/integration/utils_test.go +++ /dev/null @@ -1,348 +0,0 @@ -package docker - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "path" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/daemon" - "github.com/docker/docker/daemon/networkdriver/bridge" - "github.com/docker/docker/engine" - "github.com/docker/docker/graph" - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/registry" - "github.com/docker/docker/runconfig" - "github.com/docker/docker/utils" -) - -type Fataler interface { - Fatal(...interface{}) -} - -// This file contains utility functions for docker's unit test suite. -// It has to be named XXX_test.go, apparently, in other to access private functions -// from other XXX_test.go functions. - -// Create a temporary daemon suitable for unit testing. -// Call t.Fatal() at the first error. -func mkDaemon(f Fataler) *daemon.Daemon { - eng := newTestEngine(f, false, "") - return mkDaemonFromEngine(eng, f) -} - -func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) { - containerId, _, err := getDaemon(eng).ContainerCreate(name, config, &runconfig.HostConfig{}) - if err != nil { - f.Fatal(err) - } - return containerId -} - -func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler) (shortId string) { - return createNamedTestContainer(eng, config, f, "") -} - -func startContainer(eng *engine.Engine, id string, t Fataler) { - if err := getDaemon(eng).ContainerStart(id, &runconfig.HostConfig{}); err != nil { - t.Fatal(err) - } -} - -func containerRun(eng *engine.Engine, id string, t Fataler) { - startContainer(eng, id, t) - containerWait(eng, id, t) -} - -func containerFileExists(eng *engine.Engine, id, dir string, t Fataler) bool { - c := getContainer(eng, id, t) - if err := c.Mount(); err != nil { - t.Fatal(err) - } - defer c.Unmount() - if _, err := os.Stat(path.Join(c.RootfsPath(), dir)); err != nil { - if os.IsNotExist(err) { - return false - } - t.Fatal(err) - } - return true -} - -func containerAttach(eng *engine.Engine, id string, t Fataler) (io.WriteCloser, io.ReadCloser) { - c := getContainer(eng, id, t) - i := c.StdinPipe() - o := c.StdoutPipe() - return i, o -} - -func containerWait(eng *engine.Engine, id string, t Fataler) int { - ex, _ := getContainer(eng, id, t).WaitStop(-1 * time.Second) - return ex -} - -func containerWaitTimeout(eng *engine.Engine, id string, t Fataler) error { - _, err := getContainer(eng, id, t).WaitStop(500 * time.Millisecond) - return err -} - -func containerKill(eng *engine.Engine, id string, t Fataler) { - if err := getDaemon(eng).ContainerKill(id, 0); err != nil { - t.Fatal(err) - } -} - -func containerRunning(eng *engine.Engine, id string, t Fataler) bool { - return getContainer(eng, id, t).IsRunning() -} - -func containerAssertExists(eng *engine.Engine, id string, t Fataler) { - getContainer(eng, id, t) -} - -func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) { - daemon := mkDaemonFromEngine(eng, t) - if c, _ := daemon.Get(id); c != nil { - t.Fatal(fmt.Errorf("Container %s should not exist", id)) - } -} - -// assertHttpNotError expect the given response to not have an error. -// Otherwise the it causes the test to fail. -func assertHttpNotError(r *httptest.ResponseRecorder, t Fataler) { - // Non-error http status are [200, 400) - if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest { - t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code)) - } -} - -// assertHttpError expect the given response to have an error. -// Otherwise the it causes the test to fail. -func assertHttpError(r *httptest.ResponseRecorder, t Fataler) { - // Non-error http status are [200, 400) - if !(r.Code < http.StatusOK || r.Code >= http.StatusBadRequest) { - t.Fatal(fmt.Errorf("Unexpected http success code: %v", r.Code)) - } -} - -func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container { - daemon := mkDaemonFromEngine(eng, t) - c, err := daemon.Get(id) - if err != nil { - t.Fatal(err) - } - return c -} - -func mkDaemonFromEngine(eng *engine.Engine, t Fataler) *daemon.Daemon { - iDaemon := eng.HackGetGlobalVar("httpapi.daemon") - if iDaemon == nil { - panic("Legacy daemon field not set in engine") - } - daemon, ok := iDaemon.(*daemon.Daemon) - if !ok { - panic("Legacy daemon field in engine does not cast to *daemon.Daemon") - } - return daemon -} - -func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine { - if root == "" { - if dir, err := newTestDirectory(unitTestStoreBase); err != nil { - t.Fatal(err) - } else { - root = dir - } - } - os.MkdirAll(root, 0700) - - eng := engine.New() - eng.Logging = false - - // (This is manually copied and modified from main() until we have a more generic plugin system) - cfg := &daemon.Config{ - Root: root, - AutoRestart: autorestart, - ExecDriver: "native", - // Either InterContainerCommunication or EnableIptables must be set, - // otherwise NewDaemon will fail because of conflicting settings. - Bridge: bridge.Config{ - InterContainerCommunication: true, - }, - TrustKeyPath: filepath.Join(root, "key.json"), - LogConfig: runconfig.LogConfig{Type: "json-file"}, - } - d, err := daemon.NewDaemon(cfg, eng, registry.NewService(nil)) - if err != nil { - t.Fatal(err) - } - if err := d.Install(eng); err != nil { - t.Fatal(err) - } - return eng -} - -func NewTestEngine(t Fataler) *engine.Engine { - return newTestEngine(t, false, "") -} - -func newTestDirectory(templateDir string) (dir string, err error) { - return utils.TestDirectory(templateDir) -} - -func getCallerName(depth int) string { - return utils.GetCallerName(depth) -} - -// Write `content` to the file at path `dst`, creating it if necessary, -// as well as any missing directories. -// The file is truncated if it already exists. -// Call t.Fatal() at the first error. -func writeFile(dst, content string, t *testing.T) { - // Create subdirectories if necessary - if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) { - t.Fatal(err) - } - f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) - if err != nil { - t.Fatal(err) - } - // Write content (truncate if it exists) - if _, err := io.Copy(f, strings.NewReader(content)); err != nil { - t.Fatal(err) - } -} - -// Return the contents of file at path `src`. -// Call t.Fatal() at the first error (including if the file doesn't exist) -func readFile(src string, t *testing.T) (content string) { - f, err := os.Open(src) - if err != nil { - t.Fatal(err) - } - data, err := ioutil.ReadAll(f) - if err != nil { - t.Fatal(err) - } - return string(data) -} - -// Create a test container from the given daemon `r` and run arguments `args`. -// If the image name is "_", (eg. []string{"-i", "-t", "_", "bash"}, it is -// dynamically replaced by the current test image. -// The caller is responsible for destroying the container. -// Call t.Fatal() at the first error. -func mkContainer(r *daemon.Daemon, args []string, t *testing.T) (*daemon.Container, *runconfig.HostConfig, error) { - config, hc, _, err := parseRun(args) - defer func() { - if err != nil && t != nil { - t.Fatal(err) - } - }() - if err != nil { - return nil, nil, err - } - if config.Image == "_" { - config.Image = GetTestImage(r).ID - } - c, _, err := r.Create(config, nil, "") - if err != nil { - return nil, nil, err - } - // NOTE: hostConfig is ignored. - // If `args` specify privileged mode, custom lxc conf, external mount binds, - // port redirects etc. they will be ignored. - // This is because the correct way to set these things is to pass environment - // to the `start` job. - // FIXME: this helper function should be deprecated in favor of calling - // `create` and `start` jobs directly. - return c, hc, nil -} - -// Create a test container, start it, wait for it to complete, destroy it, -// and return its standard output as a string. -// The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. -// If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally. -func runContainer(eng *engine.Engine, r *daemon.Daemon, args []string, t *testing.T) (output string, err error) { - defer func() { - if err != nil && t != nil { - t.Fatal(err) - } - }() - container, hc, err := mkContainer(r, args, t) - if err != nil { - return "", err - } - defer r.Rm(container) - stdout := container.StdoutPipe() - defer stdout.Close() - - job := eng.Job("start", container.ID) - if err := job.ImportEnv(hc); err != nil { - return "", err - } - if err := job.Run(); err != nil { - return "", err - } - - container.WaitStop(-1 * time.Second) - data, err := ioutil.ReadAll(stdout) - if err != nil { - return "", err - } - output = string(data) - return -} - -// FIXME: this is duplicated from graph_test.go in the docker package. -func fakeTar() (io.ReadCloser, error) { - content := []byte("Hello world!\n") - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} { - hdr := new(tar.Header) - hdr.Size = int64(len(content)) - hdr.Name = name - if err := tw.WriteHeader(hdr); err != nil { - return nil, err - } - tw.Write([]byte(content)) - } - tw.Close() - return ioutil.NopCloser(buf), nil -} - -func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) []*types.Image { - config := graph.ImagesConfig{ - Filter: filter, - All: all, - } - images, err := getDaemon(eng).Repositories().Images(&config) - if err != nil { - t.Fatal(err) - } - - return images -} - -func parseRun(args []string) (*runconfig.Config, *runconfig.HostConfig, *flag.FlagSet, error) { - cmd := flag.NewFlagSet("run", flag.ContinueOnError) - cmd.SetOutput(ioutil.Discard) - cmd.Usage = nil - return runconfig.Parse(cmd, args) -} - -func getDaemon(eng *engine.Engine) *daemon.Daemon { - return eng.HackGetGlobalVar("httpapi.daemon").(*daemon.Daemon) -} diff --git a/integration/z_final_test.go b/integration/z_final_test.go deleted file mode 100644 index d6ef2884f2518..0000000000000 --- a/integration/z_final_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package docker - -import ( - "runtime" - "testing" - - "github.com/docker/docker/pkg/fileutils" -) - -func displayFdGoroutines(t *testing.T) { - t.Logf("File Descriptors: %d, Goroutines: %d", fileutils.GetTotalUsedFds(), runtime.NumGoroutine()) -} - -func TestFinal(t *testing.T) { - nuke(globalDaemon) - t.Logf("Start File Descriptors: %d, Start Goroutines: %d", startFds, startGoroutines) - displayFdGoroutines(t) -} From cd2b019214eb1978ae267786668dc7a8a3702679 Mon Sep 17 00:00:00 2001 From: "Daniel, Dao Quang Minh" Date: Wed, 8 Apr 2015 05:31:47 +0000 Subject: [PATCH 038/321] sort ports mapping before allocating prioritize the ports with static mapping before dynamic mapping. This removes the port conflicts when we allocate static port in the reserved range together with dynamic ones. When static port is allocated first, Docker will skip those when determining free ports for dynamic ones. Signed-off-by: Daniel, Dao Quang Minh --- daemon/container.go | 9 +++- integration-cli/docker_cli_run_test.go | 33 +++++++++++++ nat/sort.go | 66 +++++++++++++++++++++++++- nat/sort_test.go | 44 +++++++++++++++++ 4 files changed, 150 insertions(+), 2 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index ef42295344c44..ffae6ff2cd821 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -645,7 +645,14 @@ func (container *Container) AllocateNetwork() error { container.NetworkSettings.PortMapping = nil - for port := range portSpecs { + ports := make([]nat.Port, len(portSpecs)) + var i int + for p := range portSpecs { + ports[i] = p + i++ + } + nat.SortPortMap(ports, bindings) + for _, port := range ports { if err = container.allocatePort(port, bindings); err != nil { bridge.Release(container.ID) return err diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 0cf5c31eeeac9..1ed84545aea89 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2243,6 +2243,39 @@ func (s *DockerSuite) TestRunPortProxy(c *check.C) { } } +// https://github.com/docker/docker/issues/12148 +func (s *DockerSuite) TestRunAllocatePortInReservedRange(c *check.C) { + // allocate a dynamic port to get the most recent + cmd := exec.Command(dockerBinary, "run", "-d", "-P", "-p", "80", "busybox", "top") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + c.Fatalf("Failed to run, output: %s, error: %s", out, err) + } + id := strings.TrimSpace(out) + + cmd = exec.Command(dockerBinary, "port", id, "80") + out, _, err = runCommandWithOutput(cmd) + if err != nil { + c.Fatalf("Failed to get port, output: %s, error: %s", out, err) + } + strPort := strings.Split(strings.TrimSpace(out), ":")[1] + port, err := strconv.ParseInt(strPort, 10, 64) + if err != nil { + c.Fatalf("invalid port, got: %s, error: %s", strPort, err) + } + + // allocate a static port and a dynamic port together, with static port + // takes the next recent port in dynamic port range. + cmd = exec.Command(dockerBinary, "run", "-d", "-P", + "-p", "80", + "-p", fmt.Sprintf("%d:8080", port+1), + "busybox", "top") + out, _, err = runCommandWithOutput(cmd) + if err != nil { + c.Fatalf("Failed to run, output: %s, error: %s", out, err) + } +} + // Regression test for #7792 func (s *DockerSuite) TestRunMountOrdering(c *check.C) { testRequires(c, SameHostDaemon) diff --git a/nat/sort.go b/nat/sort.go index f36c12f7bb74e..6441936ff96d2 100644 --- a/nat/sort.go +++ b/nat/sort.go @@ -1,6 +1,10 @@ package nat -import "sort" +import ( + "sort" + "strconv" + "strings" +) type portSorter struct { ports []Port @@ -26,3 +30,63 @@ func Sort(ports []Port, predicate func(i, j Port) bool) { s := &portSorter{ports, predicate} sort.Sort(s) } + +type portMapEntry struct { + port Port + binding PortBinding +} + +type portMapSorter []portMapEntry + +func (s portMapSorter) Len() int { return len(s) } +func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// sort the port so that the order is: +// 1. port with larger specified bindings +// 2. larger port +// 3. port with tcp protocol +func (s portMapSorter) Less(i, j int) bool { + pi, pj := s[i].port, s[j].port + hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort) + return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp") +} + +// SortPortMap sorts the list of ports and their respected mapping. The ports +// will explicit HostPort will be placed first. +func SortPortMap(ports []Port, bindings PortMap) { + s := portMapSorter{} + for _, p := range ports { + if binding, ok := bindings[p]; ok { + for _, b := range binding { + s = append(s, portMapEntry{port: p, binding: b}) + } + } else { + s = append(s, portMapEntry{port: p}) + } + bindings[p] = []PortBinding{} + } + + sort.Sort(s) + var ( + i int + pm = make(map[Port]struct{}) + ) + // reorder ports + for _, entry := range s { + if _, ok := pm[entry.port]; !ok { + ports[i] = entry.port + pm[entry.port] = struct{}{} + i++ + } + // reorder bindings for this port + bindings[entry.port] = append(bindings[entry.port], entry.binding) + } +} + +func toInt(s string) int64 { + i, err := strconv.ParseInt(s, 10, 64) + if err != nil { + i = 0 + } + return i +} diff --git a/nat/sort_test.go b/nat/sort_test.go index 5d490e321bff9..ba24cdbcb9c2a 100644 --- a/nat/sort_test.go +++ b/nat/sort_test.go @@ -2,6 +2,7 @@ package nat import ( "fmt" + "reflect" "testing" ) @@ -39,3 +40,46 @@ func TestSortSamePortWithDifferentProto(t *testing.T) { t.Fail() } } + +func TestSortPortMap(t *testing.T) { + ports := []Port{ + Port("22/tcp"), + Port("22/udp"), + Port("8000/tcp"), + Port("6379/tcp"), + Port("9999/tcp"), + } + + portMap := PortMap{ + Port("22/tcp"): []PortBinding{ + {}, + }, + Port("8000/tcp"): []PortBinding{ + {}, + }, + Port("6379/tcp"): []PortBinding{ + {}, + {HostIp: "0.0.0.0", HostPort: "32749"}, + }, + Port("9999/tcp"): []PortBinding{ + {HostIp: "0.0.0.0", HostPort: "40000"}, + }, + } + + SortPortMap(ports, portMap) + if !reflect.DeepEqual(ports, []Port{ + Port("9999/tcp"), + Port("6379/tcp"), + Port("8000/tcp"), + Port("22/tcp"), + Port("22/udp"), + }) { + t.Errorf("failed to prioritize port with explicit mappings, got %v", ports) + } + if pm := portMap[Port("6379/tcp")]; !reflect.DeepEqual(pm, []PortBinding{ + {HostIp: "0.0.0.0", HostPort: "32749"}, + {}, + }) { + t.Errorf("failed to prioritize bindings with explicit mappings, got %v", pm) + } +} From 987e221607866ae391b632f9de1fd413e5a73150 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Thu, 30 Apr 2015 15:40:48 +0800 Subject: [PATCH 039/321] fix comments for test certain tests Signed-off-by: Qiang Huang --- docs/sources/project/test-and-docs.md | 4 ++-- hack/make.sh | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/sources/project/test-and-docs.md b/docs/sources/project/test-and-docs.md index 23b6b0914d6ea..66de450f55843 100644 --- a/docs/sources/project/test-and-docs.md +++ b/docs/sources/project/test-and-docs.md @@ -164,11 +164,11 @@ You can use the `TESTFLAGS` environment variable to run a single test. The flag's value is passed as arguments to the `go test` command. For example, from your local host you can run the `TestBuild` test with this command: - $ TESTFLAGS='-check.f DockerSuite.TestBuild*' make test + $ TESTFLAGS='-check.f DockerSuite.TestBuild*' make test-integration-cli To run the same test inside your Docker development container, you do this: - root@5f8630b873fe:/go/src/github.com/docker/docker# TESTFLAGS='-check.f TestBuild*' hack/make.sh + root@5f8630b873fe:/go/src/github.com/docker/docker# TESTFLAGS='-check.f TestBuild*' hack/make.sh binary test-integration-cli ## If tests under Boot2Docker fail due to disk space errors diff --git a/hack/make.sh b/hack/make.sh index 31e08cd37618a..3d92b919a86a0 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -168,7 +168,12 @@ fi # If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'. # You can use this to select certain tests to run, eg. # -# TESTFLAGS='-run ^TestBuild$' ./hack/make.sh test +# TESTFLAGS='-test.run ^TestBuild$' ./hack/make.sh test-unit +# +# For integration-cli test, we use [gocheck](https://labix.org/gocheck), if you want +# to run certain tests on your local host, you should run with command: +# +# TESTFLAGS='-check.f DockerSuite.TestBuild*' ./hack/make.sh binary test-integration-cli # go_test_dir() { dir=$1 From 424a544bb51af6013e218d67f87d27a85d46ca6d Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Thu, 30 Apr 2015 09:48:54 +0200 Subject: [PATCH 040/321] Fixing examples given for labels Fixes #12892 Signed-off-by: Vincent Demeester --- docs/sources/userguide/labels-custom-metadata.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/sources/userguide/labels-custom-metadata.md b/docs/sources/userguide/labels-custom-metadata.md index 792c2f505dabe..79ac42ebf4e8a 100644 --- a/docs/sources/userguide/labels-custom-metadata.md +++ b/docs/sources/userguide/labels-custom-metadata.md @@ -129,10 +129,14 @@ You can view the labels via the `docker inspect` command: } ... - $ docker inspect -f "{{json .Labels }}" 4fa6e0f0c678 + # Inspect labels on container + $ docker inspect -f "{{json .Config.Labels }}" 4fa6e0f0c678 {"Vendor":"ACME Incorporated","com.example.is-beta":"","com.example.version":"0.0.1-beta","com.example.release-date":"2015-02-12"} + # Inspect labels on images + $ docker inspect -f "{{json .ContainerConfig.Labels }}" myimage + ## Query labels From 1d5f1bb0f5689be2f0262163ec05930e233f0ad0 Mon Sep 17 00:00:00 2001 From: Gaurav Date: Thu, 30 Apr 2015 18:08:03 +0530 Subject: [PATCH 041/321] Make use of iptablesPath variable which has the path of iptables, instead of using string iptables directly Signed-off-by: Gaurav --- pkg/iptables/iptables.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/iptables/iptables.go b/pkg/iptables/iptables.go index 0cfcca7502231..9983ec61ff9cf 100644 --- a/pkg/iptables/iptables.go +++ b/pkg/iptables/iptables.go @@ -261,7 +261,7 @@ func Exists(table Table, chain string, rule ...string) bool { // parse "iptables -S" for the rule (this checks rules in a specific chain // in a specific table) ruleString := strings.Join(rule, " ") - existingRules, _ := exec.Command("iptables", "-t", string(table), "-S", chain).Output() + existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output() // regex to replace ips in rule // because MASQUERADE rule will not be exactly what was passed From 869ecba652294e069874c83591d6f1b469d7cc32 Mon Sep 17 00:00:00 2001 From: Lars Kellogg-Stedman Date: Tue, 28 Apr 2015 21:23:27 -0400 Subject: [PATCH 042/321] journald log driver: use CONTAINER_ID field for container id This patch modifies the journald log driver to store the container ID in a field named CONTAINER_ID, rather than (ab)using the MESSAGE_ID field. Additionally, this adds the CONTAINER_ID_FULL field containing the complete container ID and CONTAINER_NAME, containing the container name. When using the journald log driver, this permits you to see log messages from a particular container like this: # journalctl CONTAINER_ID=a9238443e193 Example output from "journalctl -o verbose" includes the following: CONTAINER_ID=27aae7361e67 CONTAINER_ID_FULL=27aae7361e67e2b4d3864280acd2b80e78daf8ec73786d8b68f3afeeaabbd4c4 CONTAINER_NAME=web Closes: #12864 Signed-off-by: Lars Kellogg-Stedman --- daemon/container.go | 2 +- daemon/logger/journald/journald.go | 12 +++- docs/mkdocs.yml | 1 + docs/sources/reference.md | 1 + docs/sources/reference/logging/journald.md | 66 ++++++++++++++++++++++ docs/sources/reference/run.md | 2 +- 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 docs/sources/reference/logging/journald.md diff --git a/daemon/container.go b/daemon/container.go index 9bd8cc1eb90cd..c7710760846d9 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -1447,7 +1447,7 @@ func (container *Container) startLogging() error { } l = dl case "journald": - dl, err := journald.New(container.ID[:12]) + dl, err := journald.New(container.ID, container.Name) if err != nil { return err } diff --git a/daemon/logger/journald/journald.go b/daemon/logger/journald/journald.go index 5eb141ac83f70..77bd2f24fec7e 100644 --- a/daemon/logger/journald/journald.go +++ b/daemon/logger/journald/journald.go @@ -11,11 +11,19 @@ type Journald struct { Jmap map[string]string } -func New(id string) (logger.Logger, error) { +func New(id string, name string) (logger.Logger, error) { if !journal.Enabled() { return nil, fmt.Errorf("journald is not enabled on this host") } - jmap := map[string]string{"MESSAGE_ID": id} + // Strip a leading slash so that people can search for + // CONTAINER_NAME=foo rather than CONTAINER_NAME=/foo. + if name[0] == '/' { + name = name[1:] + } + jmap := map[string]string{ + "CONTAINER_ID": id[:12], + "CONTAINER_ID_FULL": id, + "CONTAINER_NAME": name} return &Journald{Jmap: jmap}, nil } diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index fb08e289e1a91..7438af582b5b6 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -131,6 +131,7 @@ pages: - ['reference/builder.md', 'Reference', 'Dockerfile'] - ['faq.md', 'Reference', 'FAQ'] - ['reference/run.md', 'Reference', 'Run reference'] +- ['reference/logging/journald.md', '**HIDDEN**'] - ['compose/cli.md', 'Reference', 'Compose command line'] - ['compose/yml.md', 'Reference', 'Compose yml'] - ['compose/env.md', 'Reference', 'Compose ENV variables'] diff --git a/docs/sources/reference.md b/docs/sources/reference.md index 6c1ab462d46c8..8cfe304672159 100644 --- a/docs/sources/reference.md +++ b/docs/sources/reference.md @@ -3,6 +3,7 @@ ## Contents: - [Commands](commandline/) + - [Logging drivers](logging/) - [Dockerfile Reference](builder/) - [Docker Run Reference](run/) - [APIs](api/) diff --git a/docs/sources/reference/logging/journald.md b/docs/sources/reference/logging/journald.md new file mode 100644 index 0000000000000..9c025bfe62fa2 --- /dev/null +++ b/docs/sources/reference/logging/journald.md @@ -0,0 +1,66 @@ +# Journald logging driver + +The `journald` logging driver sends container logs to the [systemd +journal](http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html). Log entries can be retrieved using the `journalctl` +command or through use of the journal API. + +In addition to the text of the log message itself, the `journald` log +driver stores the following metadata in the journal with each message: + +| Field | Description | +----------------------|-------------| +| `CONTAINER_ID` | The container ID truncated to 12 characters. | +| `CONTAINER_ID_FULL` | The full 64-character container ID. | +| `CONTAINER_NAME` | The container name at the time it was started. If you use `docker rename` to rename a container, the new name is not reflected in the journal entries. | + +## Usage + +You can configure the default logging driver by passing the +`--log-driver` option to the Docker daemon: + + docker --log-driver=journald + +You can set the logging driver for a specific container by using the +`--log-driver` option to `docker run`: + + docker run --log-driver=journald ... + +## Note regarding container names + +The value logged in the `CONTAINER_NAME` field is the container name +that was set at startup. If you use `docker rename` to rename a +container, the new name will not be reflected in the journal entries. +Journal entries will continue to use the original name. + +## Retrieving log messages with journalctl + +You can use the `journalctl` command to retrieve log messages. You +can apply filter expressions to limit the retrieved messages to a +specific container. For example, to retrieve all log messages from a +container referenced by name: + + # journalctl CONTAINER_NAME=webserver + +You can make use of additional filters to further limit the messages +retrieved. For example, to see just those messages generated since +the system last booted: + + # journalctl -b CONTAINER_NAME=webserver + +Or to retrieve log messages in JSON format with complete metadata: + + # journalctl -o json CONTAINER_NAME=webserver + +## Retrieving log messages with the journal API + +This example uses the `systemd` Python module to retrieve container +logs: + + import systemd.journal + + reader = systemd.journal.Reader() + reader.add_match('CONTAINER_NAME=web') + + for msg in reader: + print '{CONTAINER_ID_FULL}: {MESSAGE}'.format(**msg) + diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 990faaf6c0810..7b05f99d94fc8 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -790,7 +790,7 @@ command is not available for this logging driver #### Logging driver: journald -Journald logging driver for Docker. Writes log messages to journald. `docker logs` command is not available for this logging driver +Journald logging driver for Docker. Writes log messages to journald; the container id will be stored in the journal's `CONTAINER_ID` field. `docker logs` command is not available for this logging driver. For detailed information on working with this logging driver, see [the journald logging driver](reference/logging/journald) reference documentation. ## Overriding Dockerfile image defaults From 5c86f311c88fafe87e08e58e2cd083fba127f95a Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 30 Apr 2015 20:49:28 +0200 Subject: [PATCH 043/321] Fix TestApiImagesDelete for --net none build Signed-off-by: Antonio Murdaca --- integration-cli/docker_api_images_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go index 15484715b6cee..543182eed1020 100644 --- a/integration-cli/docker_api_images_test.go +++ b/integration-cli/docker_api_images_test.go @@ -100,6 +100,7 @@ func (s *DockerSuite) TestApiImagesSaveAndLoad(c *check.C) { } func (s *DockerSuite) TestApiImagesDelete(c *check.C) { + testRequires(c, Network) name := "test-api-images-delete" out, err := buildImage(name, "FROM hello-world\nENV FOO bar", false) if err != nil { From 86d1223a29907ffc6afba557b5138cfad7816bb4 Mon Sep 17 00:00:00 2001 From: jhowardmsft Date: Thu, 23 Apr 2015 15:55:36 -0700 Subject: [PATCH 044/321] Windows: mkdirall volume path aware Signed-off-by: jhowardmsft --- api/common.go | 5 +-- pkg/system/filesys.go | 11 ++++++ pkg/system/filesys_windows.go | 64 +++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 pkg/system/filesys.go create mode 100644 pkg/system/filesys_windows.go diff --git a/api/common.go b/api/common.go index 4a9523cd45c97..743eb670910bc 100644 --- a/api/common.go +++ b/api/common.go @@ -3,13 +3,13 @@ package api import ( "fmt" "mime" - "os" "path/filepath" "sort" "strings" "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/version" "github.com/docker/libtrust" ) @@ -107,7 +107,8 @@ func MatchesContentType(contentType, expectedType string) bool { // LoadOrCreateTrustKey attempts to load the libtrust key at the given path, // otherwise generates a new one func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) { - if err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700); err != nil { + err := system.MkdirAll(filepath.Dir(trustKeyPath), 0700) + if err != nil { return nil, err } trustKey, err := libtrust.LoadKeyFile(trustKeyPath) diff --git a/pkg/system/filesys.go b/pkg/system/filesys.go new file mode 100644 index 0000000000000..e1f70e8dac1ad --- /dev/null +++ b/pkg/system/filesys.go @@ -0,0 +1,11 @@ +// +build !windows + +package system + +import ( + "os" +) + +func MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} diff --git a/pkg/system/filesys_windows.go b/pkg/system/filesys_windows.go new file mode 100644 index 0000000000000..90b500608efaa --- /dev/null +++ b/pkg/system/filesys_windows.go @@ -0,0 +1,64 @@ +// +build windows + +package system + +import ( + "os" + "regexp" + "syscall" +) + +// MkdirAll implementation that is volume path aware for Windows. +func MkdirAll(path string, perm os.FileMode) error { + if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { + return nil + } + + // The rest of this method is copied from os.MkdirAll and should be kept + // as-is to ensure compatibility. + + // Fast path: if we can tell whether path is a directory or file, stop with success or error. + dir, err := os.Stat(path) + if err == nil { + if dir.IsDir() { + return nil + } + return &os.PathError{ + Op: "mkdir", + Path: path, + Err: syscall.ENOTDIR, + } + } + + // Slow path: make sure parent exists and then call Mkdir for path. + i := len(path) + for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. + i-- + } + + j := i + for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. + j-- + } + + if j > 1 { + // Create parent + err = MkdirAll(path[0:j-1], perm) + if err != nil { + return err + } + } + + // Parent now exists; invoke Mkdir and use its result. + err = os.Mkdir(path, perm) + if err != nil { + // Handle arguments like "foo/." by + // double-checking that directory doesn't exist. + dir, err1 := os.Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + return nil +} From f9c7772b83e2382f1c3f0539180e9e6f5644fbbc Mon Sep 17 00:00:00 2001 From: John Howard Date: Fri, 24 Apr 2015 15:03:53 -0700 Subject: [PATCH 045/321] Windows: Commit() rwTar defer close Signed-off-by: John Howard --- daemon/commit.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/commit.go b/daemon/commit.go index 0c49eb2c95193..28be6828b3563 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -32,7 +32,11 @@ func (daemon *Daemon) Commit(container *Container, repository, tag, comment, aut if err != nil { return nil, err } - defer rwTar.Close() + defer func() { + if rwTar != nil { + rwTar.Close() + } + }() // Create a new image from the container's base layers + a new layer from container changes var ( From 03eb0d065db006ac1df1bca62436a844674f4d2b Mon Sep 17 00:00:00 2001 From: John Howard Date: Fri, 24 Apr 2015 16:15:18 -0700 Subject: [PATCH 046/321] Windows: Move workdir check daemon-side Signed-off-by: John Howard --- daemon/create.go | 8 ++++++++ runconfig/parse.go | 9 +-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/daemon/create.go b/daemon/create.go index db60355071866..d8addd3a99d03 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -2,6 +2,7 @@ package daemon import ( "fmt" + "path/filepath" "github.com/docker/docker/graph" "github.com/docker/docker/image" @@ -16,6 +17,13 @@ func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hos return "", warnings, err } + // The check for a valid workdir path is made on the server rather than in the + // client. This is because we don't know the type of path (Linux or Windows) + // to validate on the client. + if config.WorkingDir != "" && !filepath.IsAbs(config.WorkingDir) { + return "", warnings, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path.", config.WorkingDir) + } + container, buildWarnings, err := daemon.Create(config, hostConfig, name) if err != nil { if daemon.Graph().IsNotExist(err, config.Image) { diff --git a/runconfig/parse.go b/runconfig/parse.go index 47feac866a636..4ab406980988e 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -2,7 +2,6 @@ package runconfig import ( "fmt" - "path" "strconv" "strings" @@ -15,7 +14,6 @@ import ( ) var ( - ErrInvalidWorkingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.") ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior.") ErrConflictContainerNetworkAndDns = fmt.Errorf("Conflicting options: --net=container can't be used with --dns. This configuration is invalid.") ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: -h and the network mode (--net)") @@ -101,12 +99,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe return nil, nil, cmd, err } - // Validate input params - if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) { - return nil, nil, cmd, ErrInvalidWorkingDirectory - } - - // Validate the input mac address + // Validate input params starting with the input mac address if *flMacAddress != "" { if _, err := opts.ValidateMACAddress(*flMacAddress); err != nil { return nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress) From b255c565ca66b778e87ccf1f8d46963feaee94a0 Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 27 Apr 2015 14:27:00 -0700 Subject: [PATCH 047/321] Windows: Start refactor execdriver/driver.go Signed-off-by: John Howard --- daemon/execdriver/driver.go | 153 +---------------------------- daemon/execdriver/driver_linux.go | 156 ++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 148 deletions(-) create mode 100644 daemon/execdriver/driver_linux.go diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index ce196df2015a3..df5901ed02e7f 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -1,21 +1,14 @@ package execdriver import ( - "encoding/json" "errors" "io" - "io/ioutil" - "os" "os/exec" - "path/filepath" - "strconv" - "strings" "time" - "github.com/docker/docker/daemon/execdriver/native/template" + // TODO Windows: Factor out ulimit "github.com/docker/docker/pkg/ulimit" "github.com/docker/libcontainer" - "github.com/docker/libcontainer/cgroups/fs" "github.com/docker/libcontainer/configs" ) @@ -105,6 +98,7 @@ type NetworkInterface struct { IPv6Gateway string `json:"ipv6_gateway"` } +// TODO Windows: Factor out ulimit.Rlimit type Resources struct { Memory int64 `json:"memory"` MemorySwap int64 `json:"memory_swap"` @@ -143,6 +137,9 @@ type ProcessConfig struct { Console string `json:"-"` // dev/console path } +// TODO Windows: Factor out unused fields such as LxcConfig, AppArmorProfile, +// and CgroupParent. +// // Process wrapps an os/exec.Cmd to add more metadata type Command struct { ID string `json:"id"` @@ -168,143 +165,3 @@ type Command struct { AppArmorProfile string `json:"apparmor_profile"` CgroupParent string `json:"cgroup_parent"` // The parent cgroup for this command. } - -func InitContainer(c *Command) *configs.Config { - container := template.New() - - container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env) - container.Cgroups.Name = c.ID - container.Cgroups.AllowedDevices = c.AllowedDevices - container.Devices = c.AutoCreatedDevices - container.Rootfs = c.Rootfs - container.Readonlyfs = c.ReadonlyRootfs - - // check to see if we are running in ramdisk to disable pivot root - container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" - - // Default parent cgroup is "docker". Override if required. - if c.CgroupParent != "" { - container.Cgroups.Parent = c.CgroupParent - } - return container -} - -func getEnv(key string, env []string) string { - for _, pair := range env { - parts := strings.Split(pair, "=") - if parts[0] == key { - return parts[1] - } - } - return "" -} - -func SetupCgroups(container *configs.Config, c *Command) error { - if c.Resources != nil { - container.Cgroups.CpuShares = c.Resources.CpuShares - container.Cgroups.Memory = c.Resources.Memory - container.Cgroups.MemoryReservation = c.Resources.Memory - container.Cgroups.MemorySwap = c.Resources.MemorySwap - container.Cgroups.CpusetCpus = c.Resources.CpusetCpus - container.Cgroups.CpusetMems = c.Resources.CpusetMems - container.Cgroups.CpuQuota = c.Resources.CpuQuota - } - - return nil -} - -// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. -func getNetworkInterfaceStats(interfaceName string) (*libcontainer.NetworkInterface, error) { - out := &libcontainer.NetworkInterface{Name: interfaceName} - // This can happen if the network runtime information is missing - possible if the - // container was created by an old version of libcontainer. - if interfaceName == "" { - return out, nil - } - type netStatsPair struct { - // Where to write the output. - Out *uint64 - // The network stats file to read. - File string - } - // Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container. - netStats := []netStatsPair{ - {Out: &out.RxBytes, File: "tx_bytes"}, - {Out: &out.RxPackets, File: "tx_packets"}, - {Out: &out.RxErrors, File: "tx_errors"}, - {Out: &out.RxDropped, File: "tx_dropped"}, - - {Out: &out.TxBytes, File: "rx_bytes"}, - {Out: &out.TxPackets, File: "rx_packets"}, - {Out: &out.TxErrors, File: "rx_errors"}, - {Out: &out.TxDropped, File: "rx_dropped"}, - } - for _, netStat := range netStats { - data, err := readSysfsNetworkStats(interfaceName, netStat.File) - if err != nil { - return nil, err - } - *(netStat.Out) = data - } - return out, nil -} - -// Reads the specified statistics available under /sys/class/net//statistics -func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { - data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)) - if err != nil { - return 0, err - } - return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) -} - -func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error) { - f, err := os.Open(filepath.Join(containerDir, "state.json")) - if err != nil { - return nil, err - } - defer f.Close() - - type network struct { - Type string - HostInterfaceName string - } - - state := struct { - CgroupPaths map[string]string `json:"cgroup_paths"` - Networks []network - }{} - - if err := json.NewDecoder(f).Decode(&state); err != nil { - return nil, err - } - now := time.Now() - - mgr := fs.Manager{Paths: state.CgroupPaths} - cstats, err := mgr.GetStats() - if err != nil { - return nil, err - } - stats := &libcontainer.Stats{CgroupStats: cstats} - // if the container does not have any memory limit specified set the - // limit to the machines memory - memoryLimit := containerMemoryLimit - if memoryLimit == 0 { - memoryLimit = machineMemory - } - for _, iface := range state.Networks { - switch iface.Type { - case "veth": - istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) - if err != nil { - return nil, err - } - stats.Interfaces = append(stats.Interfaces, istats) - } - } - return &ResourceStats{ - Stats: stats, - Read: now, - MemoryLimit: memoryLimit, - }, nil -} diff --git a/daemon/execdriver/driver_linux.go b/daemon/execdriver/driver_linux.go new file mode 100644 index 0000000000000..1766d64b631af --- /dev/null +++ b/daemon/execdriver/driver_linux.go @@ -0,0 +1,156 @@ +package execdriver + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/docker/docker/daemon/execdriver/native/template" + "github.com/docker/libcontainer" + "github.com/docker/libcontainer/cgroups/fs" + "github.com/docker/libcontainer/configs" +) + +func InitContainer(c *Command) *configs.Config { + container := template.New() + + container.Hostname = getEnv("HOSTNAME", c.ProcessConfig.Env) + container.Cgroups.Name = c.ID + container.Cgroups.AllowedDevices = c.AllowedDevices + container.Devices = c.AutoCreatedDevices + container.Rootfs = c.Rootfs + container.Readonlyfs = c.ReadonlyRootfs + + // check to see if we are running in ramdisk to disable pivot root + container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" + + // Default parent cgroup is "docker". Override if required. + if c.CgroupParent != "" { + container.Cgroups.Parent = c.CgroupParent + } + return container +} + +func getEnv(key string, env []string) string { + for _, pair := range env { + parts := strings.Split(pair, "=") + if parts[0] == key { + return parts[1] + } + } + return "" +} + +func SetupCgroups(container *configs.Config, c *Command) error { + if c.Resources != nil { + container.Cgroups.CpuShares = c.Resources.CpuShares + container.Cgroups.Memory = c.Resources.Memory + container.Cgroups.MemoryReservation = c.Resources.Memory + container.Cgroups.MemorySwap = c.Resources.MemorySwap + container.Cgroups.CpusetCpus = c.Resources.CpusetCpus + container.Cgroups.CpusetMems = c.Resources.CpusetMems + container.Cgroups.CpuQuota = c.Resources.CpuQuota + } + + return nil +} + +// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo. +func getNetworkInterfaceStats(interfaceName string) (*libcontainer.NetworkInterface, error) { + out := &libcontainer.NetworkInterface{Name: interfaceName} + // This can happen if the network runtime information is missing - possible if the + // container was created by an old version of libcontainer. + if interfaceName == "" { + return out, nil + } + type netStatsPair struct { + // Where to write the output. + Out *uint64 + // The network stats file to read. + File string + } + // Ingress for host veth is from the container. Hence tx_bytes stat on the host veth is actually number of bytes received by the container. + netStats := []netStatsPair{ + {Out: &out.RxBytes, File: "tx_bytes"}, + {Out: &out.RxPackets, File: "tx_packets"}, + {Out: &out.RxErrors, File: "tx_errors"}, + {Out: &out.RxDropped, File: "tx_dropped"}, + + {Out: &out.TxBytes, File: "rx_bytes"}, + {Out: &out.TxPackets, File: "rx_packets"}, + {Out: &out.TxErrors, File: "rx_errors"}, + {Out: &out.TxDropped, File: "rx_dropped"}, + } + for _, netStat := range netStats { + data, err := readSysfsNetworkStats(interfaceName, netStat.File) + if err != nil { + return nil, err + } + *(netStat.Out) = data + } + return out, nil +} + +// Reads the specified statistics available under /sys/class/net//statistics +func readSysfsNetworkStats(ethInterface, statsFile string) (uint64, error) { + data, err := ioutil.ReadFile(filepath.Join("/sys/class/net", ethInterface, "statistics", statsFile)) + if err != nil { + return 0, err + } + return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) +} + +func Stats(containerDir string, containerMemoryLimit int64, machineMemory int64) (*ResourceStats, error) { + f, err := os.Open(filepath.Join(containerDir, "state.json")) + if err != nil { + return nil, err + } + defer f.Close() + + type network struct { + Type string + HostInterfaceName string + } + + state := struct { + CgroupPaths map[string]string `json:"cgroup_paths"` + Networks []network + }{} + + if err := json.NewDecoder(f).Decode(&state); err != nil { + return nil, err + } + now := time.Now() + + mgr := fs.Manager{Paths: state.CgroupPaths} + cstats, err := mgr.GetStats() + if err != nil { + return nil, err + } + stats := &libcontainer.Stats{CgroupStats: cstats} + // if the container does not have any memory limit specified set the + // limit to the machines memory + memoryLimit := containerMemoryLimit + if memoryLimit == 0 { + memoryLimit = machineMemory + } + for _, iface := range state.Networks { + switch iface.Type { + case "veth": + istats, err := getNetworkInterfaceStats(iface.HostInterfaceName) + if err != nil { + return nil, err + } + stats.Interfaces = append(stats.Interfaces, istats) + } + } + return &ResourceStats{ + Stats: stats, + Read: now, + MemoryLimit: memoryLimit, + }, nil +} From 71bfb9367880632fd0dbda5e37e926448473ef46 Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 27 Apr 2015 15:20:44 -0700 Subject: [PATCH 048/321] Windows: Fork execdrivers.go for Windows execdriver Signed-off-by: John Howard --- .../{execdrivers.go => execdrivers_linux.go} | 2 ++ .../execdrivers/execdrivers_windows.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) rename daemon/execdriver/execdrivers/{execdrivers.go => execdrivers_linux.go} (97%) create mode 100644 daemon/execdriver/execdrivers/execdrivers_windows.go diff --git a/daemon/execdriver/execdrivers/execdrivers.go b/daemon/execdriver/execdrivers/execdrivers_linux.go similarity index 97% rename from daemon/execdriver/execdrivers/execdrivers.go rename to daemon/execdriver/execdrivers/execdrivers_linux.go index dde0be1f0f463..89dedc762efbc 100644 --- a/daemon/execdriver/execdrivers/execdrivers.go +++ b/daemon/execdriver/execdrivers/execdrivers_linux.go @@ -1,3 +1,5 @@ +// +build linux + package execdrivers import ( diff --git a/daemon/execdriver/execdrivers/execdrivers_windows.go b/daemon/execdriver/execdrivers/execdrivers_windows.go new file mode 100644 index 0000000000000..563961fce4bcd --- /dev/null +++ b/daemon/execdriver/execdrivers/execdrivers_windows.go @@ -0,0 +1,19 @@ +// +build windows + +package execdrivers + +import ( + "fmt" + + "github.com/docker/docker/daemon/execdriver" + "github.com/docker/docker/daemon/execdriver/windows" + "github.com/docker/docker/pkg/sysinfo" +) + +func NewDriver(name, root, libPath, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) { + switch name { + case "windows": + return windows.NewDriver(root, initPath) + } + return nil, fmt.Errorf("unknown exec driver %s", name) +} From 10e2dbf375b1aebe33bce0646a3a95d34c48d4f8 Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 27 Apr 2015 15:53:21 -0700 Subject: [PATCH 049/321] Windows: Factor out LXC Signed-off-by: John Howard --- daemon/execdriver/lxc/driver.go | 2 ++ daemon/execdriver/lxc/info.go | 2 ++ daemon/execdriver/lxc/info_test.go | 2 ++ daemon/execdriver/lxc/init.go | 2 ++ daemon/execdriver/lxc/lxc_init_linux.go | 2 ++ daemon/execdriver/lxc/lxc_init_unsupported.go | 2 +- daemon/execdriver/lxc/lxc_template.go | 2 ++ 7 files changed, 13 insertions(+), 1 deletion(-) diff --git a/daemon/execdriver/lxc/driver.go b/daemon/execdriver/lxc/driver.go index 15f57bfe0faff..32e5b43ebc961 100644 --- a/daemon/execdriver/lxc/driver.go +++ b/daemon/execdriver/lxc/driver.go @@ -1,3 +1,5 @@ +// +build linux + package lxc import ( diff --git a/daemon/execdriver/lxc/info.go b/daemon/execdriver/lxc/info.go index 27b4c58604e7f..279211f324ec5 100644 --- a/daemon/execdriver/lxc/info.go +++ b/daemon/execdriver/lxc/info.go @@ -1,3 +1,5 @@ +// +build linux + package lxc import ( diff --git a/daemon/execdriver/lxc/info_test.go b/daemon/execdriver/lxc/info_test.go index edafc02511d7d..996d56b2a3e70 100644 --- a/daemon/execdriver/lxc/info_test.go +++ b/daemon/execdriver/lxc/info_test.go @@ -1,3 +1,5 @@ +// +build linux + package lxc import ( diff --git a/daemon/execdriver/lxc/init.go b/daemon/execdriver/lxc/init.go index eca1c02e21d68..a47ece97fd4d6 100644 --- a/daemon/execdriver/lxc/init.go +++ b/daemon/execdriver/lxc/init.go @@ -1,3 +1,5 @@ +// +build linux + package lxc import ( diff --git a/daemon/execdriver/lxc/lxc_init_linux.go b/daemon/execdriver/lxc/lxc_init_linux.go index e7bc2b5f3a491..fb89ac6a0a433 100644 --- a/daemon/execdriver/lxc/lxc_init_linux.go +++ b/daemon/execdriver/lxc/lxc_init_linux.go @@ -1,3 +1,5 @@ +// +build linux + package lxc import ( diff --git a/daemon/execdriver/lxc/lxc_init_unsupported.go b/daemon/execdriver/lxc/lxc_init_unsupported.go index 97bc8a984cb9f..3b7be139bb968 100644 --- a/daemon/execdriver/lxc/lxc_init_unsupported.go +++ b/daemon/execdriver/lxc/lxc_init_unsupported.go @@ -3,5 +3,5 @@ package lxc func finalizeNamespace(args *InitArgs) error { - panic("Not supported on darwin") + panic("Not supported on this platform") } diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index b3be7f8c51884..6b418b26b9e84 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -1,3 +1,5 @@ +// +build linux + package lxc import ( From 68ee5bdf96f1653d73e29329506949a765398a40 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Thu, 30 Apr 2015 14:43:14 -0700 Subject: [PATCH 050/321] Don't wrap 'cp' help too soon. Minor thing but docker cp --help was: Copy files/folders from a PATH on the container to a HOSTDIR on the host running the command. Use '-' to write the data as a tar file to STDOUT. This changes it to: Copy files/folders from a PATH on the container to a HOSTDIR on the host running the command. Use '-' to write the data as a tar file to STDOUT. The \n made the output look funky. Signed-off-by: Doug Davis --- api/client/cp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/client/cp.go b/api/client/cp.go index 392e362929e70..d195601ba697c 100644 --- a/api/client/cp.go +++ b/api/client/cp.go @@ -16,7 +16,7 @@ import ( // // Usage: docker cp CONTAINER:PATH HOSTDIR func (cli *DockerCli) CmdCp(args ...string) error { - cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTDIR|-", "Copy files/folders from a PATH on the container to a HOSTDIR on the host\nrunning the command. Use '-' to write the data\nas a tar file to STDOUT.", true) + cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTDIR|-", "Copy files/folders from a PATH on the container to a HOSTDIR on the host\nrunning the command. Use '-' to write the data as a tar file to STDOUT.", true) cmd.Require(flag.Exact, 2) cmd.ParseFlags(args, true) From 51977a230715be285f8b8076f43bd1e5803d051c Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Thu, 30 Apr 2015 15:21:05 -0700 Subject: [PATCH 051/321] Adding support for GITHUB IGNORES to the engine Dockerfile Signed-off-by: Mary Anthony --- docs/Dockerfile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index e30d4bbd540d9..a53048bb7f8b7 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -9,7 +9,7 @@ MAINTAINER Sven Dowideit (@SvenDowideit) ENV COMPOSE_BRANCH release ENV SWARM_BRANCH v0.2.0 ENV MACHINE_BRANCH master -ENV DISTRIB_BRANCH release/2.0 +ENV DISTRIB_BRANCH docs # TODO: need the full repo source to get the git version info @@ -61,7 +61,14 @@ ADD https://raw.githubusercontent.com/docker/distribution/${DISTRIB_BRANCH}/docs RUN sed -i.old '1s;^;no_version_dropdown: true;' \ /docs/sources/registry/*.md \ /docs/sources/registry/spec/*.md \ - /docs/sources/registry/spec/auth/*.md + /docs/sources/registry/spec/auth/*.md \ + /docs/sources/registry/storage-drivers/*.md + +RUN sed -i.old -e '/^/g'\ + /docs/sources/registry/*.md \ + /docs/sources/registry/spec/*.md \ + /docs/sources/registry/spec/auth/*.md \ + /docs/sources/registry/storage-drivers/*.md ####################### # Docker Swarm From cbf9a64cb5f69ce07598646ac26be247c7967cbb Mon Sep 17 00:00:00 2001 From: jhowardmsft Date: Thu, 23 Apr 2015 13:45:34 -0700 Subject: [PATCH 052/321] Windows: Change default listener to HTTP Signed-off-by: jhowardmsft --- docker/docker.go | 10 ++++++++-- opts/opts.go | 10 +++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docker/docker.go b/docker/docker.go index 1096b840f8468..698991e0539e6 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "os" + "runtime" "strings" "github.com/Sirupsen/logrus" @@ -62,8 +63,13 @@ func main() { if len(flHosts) == 0 { defaultHost := os.Getenv("DOCKER_HOST") if defaultHost == "" || *flDaemon { - // If we do not have a host, default to unix socket - defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket) + if runtime.GOOS != "windows" { + // If we do not have a host, default to unix socket + defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket) + } else { + // If we do not have a host, default to TCP socket on Windows + defaultHost = fmt.Sprintf("tcp://%s:%d", opts.DefaultHTTPHost, opts.DefaultHTTPPort) + } } defaultHost, err := opts.ValidateHost(defaultHost) if err != nil { diff --git a/opts/opts.go b/opts/opts.go index d2c32f13c7229..1db454736e5bd 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -14,9 +14,13 @@ import ( ) var ( - alphaRegexp = regexp.MustCompile(`[a-zA-Z]`) - domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) - DefaultHTTPHost = "127.0.0.1" // Default HTTP Host used if only port is provided to -H flag e.g. docker -d -H tcp://:8080 + alphaRegexp = regexp.MustCompile(`[a-zA-Z]`) + domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) + DefaultHTTPHost = "127.0.0.1" // Default HTTP Host used if only port is provided to -H flag e.g. docker -d -H tcp://:8080 + // TODO Windows. DefaultHTTPPort is only used on Windows if a -H parameter + // is not supplied. A better longer term solution would be to use a named + // pipe as the default on the Windows daemon. + DefaultHTTPPort = 2375 // Default HTTP Port DefaultUnixSocket = "/var/run/docker.sock" // Docker daemon by default always listens on the default unix socket ) From 04adaaf1ee1688ff48cb8f541dcb80e965f45080 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Fri, 1 May 2015 09:16:31 -0400 Subject: [PATCH 053/321] devmapper: Disable mount option "discard" by default Right now devicemapper mounts thin device using online discards by default and passes mount option "discard". Generally people discourage usage of online discards as they can be a drain on performance. Instead it is recommended to use fstrim once in a while to reclaim the space. In case of containers, we recommend to keep data volumes separate. So there might not be lot of rm, unlink operations going on and there might not be lot of space being freed by containers. So it might not matter much if we don't reclaim that free space in pool. User can still pass mount option explicitly using dm.mountopt=discard to enable discards if they would like to. So this is more like setting the containers by default for better performance instead of better space efficiency in pool. And user can change the behavior if they don't like default behavior. Reported-by: Mike Snitzer Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/deviceset.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index b5d67fa119d0c..c2a9a44feefc2 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -1366,11 +1366,7 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error { options = joinMountOptions(options, devices.mountOptions) options = joinMountOptions(options, label.FormatMountLabel("", mountLabel)) - err = syscall.Mount(info.DevName(), path, fstype, flags, joinMountOptions("discard", options)) - if err != nil && err == syscall.EINVAL { - err = syscall.Mount(info.DevName(), path, fstype, flags, options) - } - if err != nil { + if err := syscall.Mount(info.DevName(), path, fstype, flags, options); err != nil { return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err) } From 79f13d1497b073113f713e903e934dafcf78c42f Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Fri, 1 May 2015 22:26:40 +0800 Subject: [PATCH 054/321] fix docker rm name issue Addresses https://github.com/docker/docker/issues/12308 Signed-off-by: Qiang Huang --- api/client/rm.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/client/rm.go b/api/client/rm.go index 1ecc0d65727c1..d99f32e2fda9c 100644 --- a/api/client/rm.go +++ b/api/client/rm.go @@ -3,6 +3,7 @@ package client import ( "fmt" "net/url" + "strings" flag "github.com/docker/docker/pkg/mflag" ) @@ -36,6 +37,7 @@ func (cli *DockerCli) CmdRm(args ...string) error { if name == "" { return fmt.Errorf("Container name cannot be empty") } + name = strings.Trim(name, "/") _, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil)) if err != nil { From 5eee4058feaa7f06348941d267f4d76178ab3de4 Mon Sep 17 00:00:00 2001 From: Megan Kostick Date: Wed, 29 Apr 2015 12:51:57 -0700 Subject: [PATCH 055/321] Docs adding uninstall instructions Signed-off-by: Megan Kostick --- docs/sources/installation/SUSE.md | 16 +- docs/sources/installation/archlinux.md | 22 ++- docs/sources/installation/centos.md | 37 ++++- docs/sources/installation/cruxlinux.md | 24 ++- docs/sources/installation/debian.md | 36 ++++ docs/sources/installation/fedora.md | 42 ++++- docs/sources/installation/frugalware.md | 20 ++- docs/sources/installation/gentoolinux.md | 18 ++ docs/sources/installation/mac.md | 202 +++++++++++++---------- docs/sources/installation/oracle.md | 36 ++-- docs/sources/installation/rhel.md | 37 ++++- docs/sources/installation/ubuntulinux.md | 93 ++++++----- docs/sources/installation/windows.md | 10 +- 13 files changed, 434 insertions(+), 159 deletions(-) diff --git a/docs/sources/installation/SUSE.md b/docs/sources/installation/SUSE.md index 756ed6b5c1430..106d4cbe31748 100644 --- a/docs/sources/installation/SUSE.md +++ b/docs/sources/installation/SUSE.md @@ -28,7 +28,7 @@ Docker is available in **SUSE Linux Enterprise 12 and later**. Please note that due to its current limitations Docker is able to run only on **64 bit** architecture. -# Installation +## Installation Install the Docker package. @@ -76,6 +76,20 @@ If you need to add an HTTP Proxy, set a different directory or partition for the Docker runtime files, or make other customizations, read our systemd article to learn how to [customize your systemd Docker daemon options](/articles/systemd/). +## Uninstallation + +To uninstall the Docker package: + + $ sudo zypper rm docker + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## What's next Continue with the [User Guide](/userguide/). diff --git a/docs/sources/installation/archlinux.md b/docs/sources/installation/archlinux.md index 99849c7aa0c7a..570e36c482466 100644 --- a/docs/sources/installation/archlinux.md +++ b/docs/sources/installation/archlinux.md @@ -30,13 +30,13 @@ in the packages. The core dependencies are: For the normal package a simple - pacman -S docker + $ sudo pacman -S docker is all that is needed. For the AUR package execute: - yaourt -S docker-git + $ sudo yaourt -S docker-git The instructions here assume **yaourt** is installed. See [Arch User Repository](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages) @@ -59,3 +59,21 @@ To start on system boot: If you need to add an HTTP Proxy, set a different directory or partition for the Docker runtime files, or make other customizations, read our systemd article to learn how to [customize your systemd Docker daemon options](/articles/systemd/). + +## Uninstallation + +To uninstall the Docker package: + + $ sudo pacman -R docker + +To uninstall the Docker package and dependencies that are no longer needed: + + $ sudo pacman -Rns docker + +The above commands will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. diff --git a/docs/sources/installation/centos.md b/docs/sources/installation/centos.md index 7868f11b05727..efebad503c5e1 100644 --- a/docs/sources/installation/centos.md +++ b/docs/sources/installation/centos.md @@ -25,7 +25,10 @@ To run Docker on [CentOS-6.5](http://www.centos.org) or later, you will need kernel version 2.6.32-431 or higher as this has specific kernel fixes to allow Docker to run. -## Installing Docker - CentOS-7 +## CentOS-7 + +### Installation + Docker is included by default in the CentOS-Extras repository. To install run the following command: @@ -33,7 +36,23 @@ run the following command: Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). -## Installing Docker - CentOS-6.5 +### Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + +## CentOS-6.5 + +### Installation For CentOS-6.5, the Docker package is part of [Extra Packages for Enterprise Linux (EPEL)](https://fedoraproject.org/wiki/EPEL) repository, @@ -57,6 +76,20 @@ Next, let's install the `docker-io` package which will install Docker on our hos Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). +### Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker-io + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Manual installation of latest Docker release While using a package is the recommended way of installing Docker, diff --git a/docs/sources/installation/cruxlinux.md b/docs/sources/installation/cruxlinux.md index d474aa52f873a..e03715009f605 100644 --- a/docs/sources/installation/cruxlinux.md +++ b/docs/sources/installation/cruxlinux.md @@ -15,9 +15,9 @@ The `docker` port will build and install the latest tagged version of Docker. ## Installation -Assuming you have contrib enabled, update your ports tree and install docker (*as root*): +Assuming you have contrib enabled, update your ports tree and install docker: - # prt-get depinst docker + $ sudo prt-get depinst docker ## Kernel requirements @@ -27,7 +27,7 @@ the necessary modules enabled for the Docker Daemon to function correctly. Please read the `README`: - $ prt-get readme docker + $ sudo prt-get readme docker The `docker` port installs the `contrib/check-config.sh` script provided by the Docker contributors for checking your kernel @@ -39,9 +39,9 @@ To check your Kernel configuration run: ## Starting Docker -There is a rc script created for Docker. To start the Docker service (*as root*): +There is a rc script created for Docker. To start the Docker service: - # /etc/rc.d/docker start + $ sudo /etc/rc.d/docker start To start on system boot: @@ -60,6 +60,20 @@ or use it as part of your `FROM` line in your `Dockerfile(s)`. There are also user contributed [CRUX based image(s)](https://registry.hub.docker.com/repos/crux/) on the Docker Hub. +## Uninstallation + +To uninstall the Docker package: + + $ sudo prt-get remove docker + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Issues If you have any issues please file a bug with the diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md index da9e5f59b132b..883f920cdd893 100644 --- a/docs/sources/installation/debian.md +++ b/docs/sources/installation/debian.md @@ -37,6 +37,24 @@ container runs, it prints an informational message. Then, it exits. > If you want to enable memory and swap accounting see > [this](/installation/ubuntulinux/#memory-and-swap-accounting). +### Uninstallation + +To uninstall the Docker package: + + $ sudo apt-get purge docker-io + +To uninstall the Docker package and dependencies that are no longer needed: + + $ sudo apt-get autoremove --purge docker-io + +The above commands will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Debian Wheezy/Stable 7.x (64-bit) Docker requires Kernel 3.8+, while Wheezy ships with Kernel 3.2 (for more details @@ -74,6 +92,24 @@ which is officially supported by Docker. > > $ wget -qO- https://get.docker.com/gpg | sudo apt-key add - +### Uninstallation + +To uninstall the Docker package: + + $ sudo apt-get purge lxc-docker + +To uninstall the Docker package and dependencies that are no longer needed: + + $ sudo apt-get autoremove --purge lxc-docker + +The above commands will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Giving non-root access The `docker` daemon always runs as the `root` user and the `docker` diff --git a/docs/sources/installation/fedora.md b/docs/sources/installation/fedora.md index ed4e8372a4527..b3f23e4514c0d 100644 --- a/docs/sources/installation/fedora.md +++ b/docs/sources/installation/fedora.md @@ -13,19 +13,37 @@ Currently the Fedora project will only support Docker when running on kernels shipped by the distribution. There are kernel changes which will cause issues if one decides to step outside that box and run non-distribution kernel packages. -## Fedora 21 and later installation +## Fedora 21 and later -Install the `docker` package which will install Docker on our host. +### Installation + +Install the Docker package which will install Docker on our host. $ sudo yum -y install docker -To update the `docker` package: +To update the Docker package: $ sudo yum -y update docker Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). -## Fedora 20 installation +### Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + +## Fedora 20 + +### Installation For `Fedora 20`, there is a package name conflict with a system tray application and its executable, so the Docker RPM package was called `docker-io`. @@ -36,12 +54,26 @@ package first. $ sudo yum -y remove docker $ sudo yum -y install docker-io -To update the `docker` package: +To update the Docker package: $ sudo yum -y update docker-io Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). +### Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker-io + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Starting the Docker daemon Now that it's installed, let's start the Docker daemon. diff --git a/docs/sources/installation/frugalware.md b/docs/sources/installation/frugalware.md index 6b4db23b262e9..c7002803469e5 100644 --- a/docs/sources/installation/frugalware.md +++ b/docs/sources/installation/frugalware.md @@ -28,7 +28,7 @@ in the packages. The core dependencies are: A simple - pacman -S lxc-docker + $ sudo pacman -S lxc-docker is all that is needed. @@ -48,3 +48,21 @@ To start on system boot: If you need to add an HTTP Proxy, set a different directory or partition for the Docker runtime files, or make other customizations, read our systemd article to learn how to [customize your systemd Docker daemon options](/articles/systemd/). + +## Uninstallation + +To uninstall the Docker package: + + $ sudo pacman -R lxc-docker + +To uninstall the Docker package and dependencies that are no longer needed: + + $ sudo pacman -Rns lxc-docker + +The above commands will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. diff --git a/docs/sources/installation/gentoolinux.md b/docs/sources/installation/gentoolinux.md index 716eab9d8254d..865e8eb00843f 100644 --- a/docs/sources/installation/gentoolinux.md +++ b/docs/sources/installation/gentoolinux.md @@ -95,3 +95,21 @@ To start on system boot: If you need to add an HTTP Proxy, set a different directory or partition for the Docker runtime files, or make other customizations, read our systemd article to learn how to [customize your systemd Docker daemon options](/articles/systemd/). + +## Uninstallation + +To uninstall the Docker package: + + $ sudo emerge -cav app-emulation/docker + +To uninstall the Docker package and dependencies that are no longer needed: + + $ sudo emerge -C app-emulation/docker + +The above commands will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. diff --git a/docs/sources/installation/mac.md b/docs/sources/installation/mac.md index 4b157c1682c60..0b4274c2d3c3d 100644 --- a/docs/sources/installation/mac.md +++ b/docs/sources/installation/mac.md @@ -2,7 +2,7 @@ page_title: Installation on Mac OS X page_description: Instructions for installing Docker on OS X using boot2docker. page_keywords: Docker, Docker documentation, requirements, boot2docker, VirtualBox, SSH, Linux, OSX, OS X, Mac -# Install Docker on Mac OS X +# Mac OS X You can install Docker using Boot2Docker to run `docker` commands at your command-line. Choose this installation if you are familiar with the command-line or plan to @@ -55,17 +55,17 @@ When you start the `boot2docker` process, the VM is assigned an IP address. Unde practice, work through the exercises on this page. -### Install Boot2Docker +### Installation 1. Go to the [boot2docker/osx-installer ]( -https://github.com/boot2docker/osx-installer/releases/latest) release page. + https://github.com/boot2docker/osx-installer/releases/latest) release page. 4. Download Boot2Docker by clicking `Boot2Docker-x.x.x.pkg` in the "Downloads" -section. + section. 3. Install Boot2Docker by double-clicking the package. - The installer places Boot2Docker in your "Applications" folder. + The installer places Boot2Docker in your "Applications" folder. The installation places the `docker` and `boot2docker` binaries in your `/usr/local/bin` directory. @@ -96,30 +96,32 @@ application: Once the launch completes, you can run `docker` commands. A good way to verify your setup succeeded is to run the `hello-world` container. - $ docker run hello-world - Unable to find image 'hello-world:latest' locally - 511136ea3c5a: Pull complete - 31cbccb51277: Pull complete - e45a5af57b00: Pull complete - hello-world:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security. - Status: Downloaded newer image for hello-world:latest - Hello from Docker. - This message shows that your installation appears to be working correctly. - - To generate this message, Docker took the following steps: - 1. The Docker client contacted the Docker daemon. - 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. - (Assuming it was not already locally available.) - 3. The Docker daemon created a new container from that image which runs the - executable that produces the output you are currently reading. - 4. The Docker daemon streamed that output to the Docker client, which sent it - to your terminal. - - To try something more ambitious, you can run an Ubuntu container with: - $ docker run -it ubuntu bash - - For more examples and ideas, visit: - http://docs.docker.com/userguide/ + $ docker run hello-world + Unable to find image 'hello-world:latest' locally + 511136ea3c5a: Pull complete + 31cbccb51277: Pull complete + e45a5af57b00: Pull complete + hello-world:latest: The image you are pulling has been verified. + Important: image verification is a tech preview feature and should not be + relied on to provide security. + Status: Downloaded newer image for hello-world:latest + Hello from Docker. + This message shows that your installation appears to be working correctly. + + To generate this message, Docker took the following steps: + 1. The Docker client contacted the Docker daemon. + 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. + (Assuming it was not already locally available.) + 3. The Docker daemon created a new container from that image which runs the + executable that produces the output you are currently reading. + 4. The Docker daemon streamed that output to the Docker client, which sent it + to your terminal. + + To try something more ambitious, you can run an Ubuntu container with: + $ docker run -it ubuntu bash + + For more examples and ideas, visit: + http://docs.docker.com/userguide/ A more typical way to start and stop `boot2docker` is using the command line. @@ -130,36 +132,36 @@ Initialize and run `boot2docker` from the command line, do the following: 1. Create a new Boot2Docker VM. - $ boot2docker init + $ boot2docker init - This creates a new virtual machine. You only need to run this command once. + This creates a new virtual machine. You only need to run this command once. 2. Start the `boot2docker` VM. - $ boot2docker start + $ boot2docker start 3. Display the environment variables for the Docker client. - $ boot2docker shellinit - Writing /Users/mary/.boot2docker/certs/boot2docker-vm/ca.pem - Writing /Users/mary/.boot2docker/certs/boot2docker-vm/cert.pem - Writing /Users/mary/.boot2docker/certs/boot2docker-vm/key.pem - export DOCKER_HOST=tcp://192.168.59.103:2376 - export DOCKER_CERT_PATH=/Users/mary/.boot2docker/certs/boot2docker-vm - export DOCKER_TLS_VERIFY=1 + $ boot2docker shellinit + Writing /Users/mary/.boot2docker/certs/boot2docker-vm/ca.pem + Writing /Users/mary/.boot2docker/certs/boot2docker-vm/cert.pem + Writing /Users/mary/.boot2docker/certs/boot2docker-vm/key.pem + export DOCKER_HOST=tcp://192.168.59.103:2376 + export DOCKER_CERT_PATH=/Users/mary/.boot2docker/certs/boot2docker-vm + export DOCKER_TLS_VERIFY=1 - The specific paths and address on your machine will be different. + The specific paths and address on your machine will be different. 4. To set the environment variables in your shell do the following: - $ eval "$(boot2docker shellinit)" + $ eval "$(boot2docker shellinit)" - You can also set them manually by using the `export` commands `boot2docker` - returns. + You can also set them manually by using the `export` commands `boot2docker` + returns. 5. Run the `hello-world` container to verify your setup. - $ docker run hello-world + $ docker run hello-world ## Basic Boot2Docker exercises @@ -167,8 +169,8 @@ Initialize and run `boot2docker` from the command line, do the following: At this point, you should have `boot2docker` running and the `docker` client environment initialized. To verify this, run the following commands: - $ boot2docker status - $ docker version + $ boot2docker status + $ docker version Work through this section to try some practical container tasks using `boot2docker` VM. @@ -176,52 +178,52 @@ Work through this section to try some practical container tasks using `boot2dock 1. Start an NGINX container on the DOCKER_HOST. - $ docker run -d -P --name web nginx + $ docker run -d -P --name web nginx - Normally, the `docker run` commands starts a container, runs it, and then - exits. The `-d` flag keeps the container running in the background - after the `docker run` command completes. The `-P` flag publishes exposed ports from the - container to your local host; this lets you access them from your Mac. + Normally, the `docker run` commands starts a container, runs it, and then + exits. The `-d` flag keeps the container running in the background + after the `docker run` command completes. The `-P` flag publishes exposed ports from the + container to your local host; this lets you access them from your Mac. 2. Display your running container with `docker ps` command - CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES - 5fb65ff765e9 nginx:latest "nginx -g 'daemon of 3 minutes ago Up 3 minutes 0.0.0.0:49156->443/tcp, 0.0.0.0:49157->80/tcp web + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 5fb65ff765e9 nginx:latest "nginx -g 'daemon of 3 minutes ago Up 3 minutes 0.0.0.0:49156->443/tcp, 0.0.0.0:49157->80/tcp web - At this point, you can see `nginx` is running as a daemon. + At this point, you can see `nginx` is running as a daemon. 3. View just the container's ports. - $ docker port web - 443/tcp -> 0.0.0.0:49156 - 80/tcp -> 0.0.0.0:49157 + $ docker port web + 443/tcp -> 0.0.0.0:49156 + 80/tcp -> 0.0.0.0:49157 - This tells you that the `web` container's port `80` is mapped to port - `49157` on your Docker host. + This tells you that the `web` container's port `80` is mapped to port + `49157` on your Docker host. 4. Enter the `http://localhost:49157` address (`localhost` is `0.0.0.0`) in your browser: - ![Bad Address](/installation/images/bad_host.png) + ![Bad Address](/installation/images/bad_host.png) - This didn't work. The reason it doesn't work is your `DOCKER_HOST` address is - not the localhost address (0.0.0.0) but is instead the address of the - `boot2docker` VM. + This didn't work. The reason it doesn't work is your `DOCKER_HOST` address is + not the localhost address (0.0.0.0) but is instead the address of the + `boot2docker` VM. 5. Get the address of the `boot2docker` VM. - $ boot2docker ip - 192.168.59.103 + $ boot2docker ip + 192.168.59.103 6. Enter the `http://192.168.59.103:49157` address in your browser: - ![Correct Addressing](/installation/images/good_host.png) + ![Correct Addressing](/installation/images/good_host.png) - Success! + Success! 7. To stop and then remove your running `nginx` container, do the following: - $ docker stop web - $ docker rm web + $ docker stop web + $ docker rm web ### Mount a volume on the container @@ -231,46 +233,46 @@ The next exercise demonstrates how to do this. 1. Change to your user `$HOME` directory. - $ cd $HOME + $ cd $HOME 2. Make a new `site` directory. - $ mkdir site + $ mkdir site 3. Change into the `site` directory. - $ cd site + $ cd site 4. Create a new `index.html` file. - $ echo "my new site" > index.html + $ echo "my new site" > index.html 5. Start a new `nginx` container and replace the `html` folder with your `site` directory. - $ docker run -d -P -v $HOME/site:/usr/share/nginx/html --name mysite nginx + $ docker run -d -P -v $HOME/site:/usr/share/nginx/html --name mysite nginx 6. Get the `mysite` container's port. - $ docker port mysite - 80/tcp -> 0.0.0.0:49166 - 443/tcp -> 0.0.0.0:49165 + $ docker port mysite + 80/tcp -> 0.0.0.0:49166 + 443/tcp -> 0.0.0.0:49165 7. Open the site in a browser: - ![My site page](/installation/images/newsite_view.png) + ![My site page](/installation/images/newsite_view.png) 8. Try adding a page to your `$HOME/site` in real time. - $ echo "This is cool" > cool.html + $ echo "This is cool" > cool.html 9. Open the new page in the browser. - ![Cool page](/installation/images/cool_view.png) + ![Cool page](/installation/images/cool_view.png) 9. Stop and then remove your running `mysite` container. - $ docker stop mysite - $ docker rm mysite + $ docker stop mysite + $ docker rm mysite ## Upgrade Boot2Docker @@ -286,11 +288,11 @@ To upgrade from 1.4.1 or greater, you can do this: 2. Stop the `boot2docker` application. - $ boot2docker stop + $ boot2docker stop 3. Run the upgrade command. - $ boot2docker upgrade + $ boot2docker upgrade ### Use the installer @@ -301,21 +303,45 @@ To upgrade any version of Boot2Docker, do this: 2. Stop the `boot2docker` application. - $ boot2docker stop + $ boot2docker stop 3. Go to the [boot2docker/osx-installer ]( https://github.com/boot2docker/osx-installer/releases/latest) release page. 4. Download Boot2Docker by clicking `Boot2Docker-x.x.x.pkg` in the "Downloads" -section. + section. 2. Install Boot2Docker by double-clicking the package. - The installer places Boot2Docker in your "Applications" folder. + The installer places Boot2Docker in your "Applications" folder. -## Learning more and acknowledgement +## Uninstallation + +1. Go to the [boot2docker/osx-installer ]( + https://github.com/boot2docker/osx-installer/releases/latest) release page. + +2. Download the source code by clicking `Source code (zip)` or + `Source code (tar.gz)` in the "Downloads" section. + +3. Extract the source code. +4. Open a terminal on your local machine. + +5. Change to the directory where you extracted the source code: + + $ cd + +6. Make sure the uninstall.sh script is executable: + + $ chmod +x uninstall.sh + +7. Run the uninstall.sh script: + + $ ./uninstall.sh + + +## Learning more and acknowledgement Use `boot2docker help` to list the full command line reference. For more information about using SSH or SCP to access the Boot2Docker VM, see the README diff --git a/docs/sources/installation/oracle.md b/docs/sources/installation/oracle.md index e05e664c120dc..e74decd9b9f26 100644 --- a/docs/sources/installation/oracle.md +++ b/docs/sources/installation/oracle.md @@ -43,35 +43,35 @@ To enable the *addons* repository: `/etc/yum.repos.d/public-yum-ol7.repo` and set `enabled=1` in the `[ol6_addons]` or the `[ol7_addons]` stanza. -## To install Docker: +## Installation 1. Ensure the appropriate *addons* channel or repository has been enabled. 2. Use yum to install the Docker package: - $ sudo yum install docker + $ sudo yum install docker -## To start Docker: +## Starting Docker 1. Now that it's installed, start the Docker daemon: - 1. On Oracle Linux 6: + 1. On Oracle Linux 6: - $ sudo service docker start + $ sudo service docker start - 2. On Oracle Linux 7: + 2. On Oracle Linux 7: - $ sudo systemctl start docker.service + $ sudo systemctl start docker.service 2. If you want the Docker daemon to start automatically at boot: - 1. On Oracle Linux 6: + 1. On Oracle Linux 6: - $ sudo chkconfig docker on + $ sudo chkconfig docker on - 2. On Oracle Linux 7: + 2. On Oracle Linux 7: - $ sudo systemctl enable docker.service + $ sudo systemctl enable docker.service **Done!** @@ -99,6 +99,20 @@ To enable btrfs support on Oracle Linux: You can now continue with the [Docker User Guide](/userguide/). +## Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Known issues ### Docker unmounts btrfs filesystem on shutdown diff --git a/docs/sources/installation/rhel.md b/docs/sources/installation/rhel.md index b3bd7aa1d0852..9b1734692192a 100644 --- a/docs/sources/installation/rhel.md +++ b/docs/sources/installation/rhel.md @@ -16,7 +16,9 @@ running on kernels shipped by the distribution. There are kernel changes which will cause issues if one decides to step outside that box and run non-distribution kernel packages. -## Red Hat Enterprise Linux 7 installation +## Red Hat Enterprise Linux 7 + +### Installation **Red Hat Enterprise Linux 7 (64 bit)** has [shipped with Docker](https://access.redhat.com/site/products/red-hat-enterprise-linux/docker-and-containers). @@ -41,7 +43,21 @@ Portal](https://access.redhat.com/). Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). -## Red Hat Enterprise Linux 6.6 installation +### Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + +## Red Hat Enterprise Linux 6.6 You will need **64 bit** [RHEL 6.6](https://access.redhat.com/site/articles/3078#RHEL6) or later, with @@ -66,7 +82,7 @@ non-distro kernel packages. > vulnerabilities and severe bugs (such as those found in kernel 2.6.32) > are fixed. -## Installation +### Installation Firstly, you need to install the EPEL repository. Please follow the [EPEL installation @@ -90,6 +106,20 @@ To update the `docker-io` package Please continue with the [Starting the Docker daemon](#starting-the-docker-daemon). +### Uninstallation + +To uninstall the Docker package: + + $ sudo yum -y remove docker-io + +The above command will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. + ## Starting the Docker daemon Now that it's installed, let's start the Docker daemon. @@ -118,7 +148,6 @@ If you need to add an HTTP Proxy, set a different directory or partition for the Docker runtime files, or make other customizations, read our Systemd article to learn how to [customize your Systemd Docker daemon options](/articles/systemd/). - ## Issues? If you have any issues - please report them directly in the diff --git a/docs/sources/installation/ubuntulinux.md b/docs/sources/installation/ubuntulinux.md index 6c854997fc253..652edc9fd8824 100644 --- a/docs/sources/installation/ubuntulinux.md +++ b/docs/sources/installation/ubuntulinux.md @@ -28,8 +28,8 @@ and frequently panic under certain conditions. To check your current kernel version, open a terminal and use `uname -r` to display your kernel version: - $ uname -r - 3.11.0-15-generic + $ uname -r + 3.11.0-15-generic >**Caution** Some Ubuntu OS versions **require a version higher than 3.10** to >run Docker, see the prerequisites on this page that apply to your Ubuntu @@ -72,17 +72,17 @@ To upgrade your kernel and install the additional packages, do the following: 2. Update your package manager. - $ sudo apt-get update + $ sudo apt-get update 3. Install both the required and optional packages. - $ sudo apt-get install linux-image-generic-lts-trusty + $ sudo apt-get install linux-image-generic-lts-trusty - Depending on your environment, you may install more as described in the preceding table. + Depending on your environment, you may install more as described in the preceding table. 4. Reboot your host. - $ sudo reboot + $ sudo reboot 5. After your system reboots, go ahead and [install Docker](#installing-docker-on-ubuntu). @@ -92,7 +92,7 @@ To upgrade your kernel and install the additional packages, do the following: Docker uses AUFS as the default storage backend. If you don't have this prerequisite installed, Docker's installation process adds it. -##Installing Docker on Ubuntu +##Installation Make sure you have installed the prerequisites for your Ubuntu version. Then, install Docker using the following: @@ -101,19 +101,19 @@ install Docker using the following: 2. Verify that you have `wget` installed. - $ which wget + $ which wget - If `wget` isn't installed, install it after updating your manager: + If `wget` isn't installed, install it after updating your manager: - $ sudo apt-get update - $ sudo apt-get install wget + $ sudo apt-get update + $ sudo apt-get install wget 3. Get the latest Docker package. - $ wget -qO- https://get.docker.com/ | sh + $ wget -qO- https://get.docker.com/ | sh - The system prompts you for your `sudo` password. Then, it downloads and - installs Docker and its dependencies. + The system prompts you for your `sudo` password. Then, it downloads and + installs Docker and its dependencies. >**Note**: If your company is behind a filtering proxy, you may find that the >`apt-key` >command fails for the Docker repo during installation. To work around this, @@ -123,9 +123,9 @@ install Docker using the following: 4. Verify `docker` is installed correctly. - $ sudo docker run hello-world + $ sudo docker run hello-world - This command downloads a test image and runs it in a container. + This command downloads a test image and runs it in a container. ## Optional configurations for Docker on Ubuntu @@ -155,19 +155,19 @@ To create the `docker` group and add your user: 1. Log into Ubuntu as a user with `sudo` privileges. - This procedure assumes you log in as the `ubuntu` user. + This procedure assumes you log in as the `ubuntu` user. 3. Create the `docker` group and add your user. - $ sudo usermod -aG docker ubuntu + $ sudo usermod -aG docker ubuntu 3. Log out and log back in. - This ensures your user is running with the correct permissions. + This ensures your user is running with the correct permissions. 4. Verify your work by running `docker` without `sudo`. - $ docker run hello-world + $ docker run hello-world ### Adjust memory and swap accounting @@ -187,13 +187,13 @@ following. 3. Set the `GRUB_CMDLINE_LINUX` value as follows: - GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1" + GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1" 4. Save and close the file. 5. Update GRUB. - $ sudo update-grub + $ sudo update-grub 6. Reboot your system. @@ -216,25 +216,25 @@ To configure UFW and allow incoming connections on the Docker port: 2. Verify that UFW is installed and enabled. - $ sudo ufw status + $ sudo ufw status 3. Open the `/etc/default/ufw` file for editing. - $ sudo nano /etc/default/ufw + $ sudo nano /etc/default/ufw 4. Set the `DEFAULT_FORWARD_POLICY` policy to: - DEFAULT_FORWARD_POLICY="ACCEPT" + DEFAULT_FORWARD_POLICY="ACCEPT" 5. Save and close the file. 6. Reload UFW to use the new setting. - $ sudo ufw reload + $ sudo ufw reload 7. Allow incoming connections on the Docker port. - $ sudo ufw allow 2375/tcp + $ sudo ufw allow 2375/tcp ### Configure a DNS server for use by Docker @@ -262,25 +262,25 @@ To specify a DNS server for use by Docker: 2. Open the `/etc/default/docker` file for editing. - $ sudo nano /etc/default/docker + $ sudo nano /etc/default/docker 3. Add a setting for Docker. - DOCKER_OPTS="--dns 8.8.8.8" + DOCKER_OPTS="--dns 8.8.8.8" Replace `8.8.8.8` with a local DNS server such as `192.168.1.1`. You can also specify multiple DNS servers. Separated them with spaces, for example: - --dns 8.8.8.8 --dns 192.168.1.1 + --dns 8.8.8.8 --dns 192.168.1.1 - >**Warning**: If you're doing this on a laptop which connects to various - >networks, make sure to choose a public DNS server. + >**Warning**: If you're doing this on a laptop which connects to various + >networks, make sure to choose a public DNS server. 4. Save and close the file. 5. Restart the Docker daemon. - $ sudo restart docker + $ sudo restart docker   @@ -291,22 +291,39 @@ NetworkManager (this might slow your network). 1. Open the `/etc/NetworkManager/NetworkManager.conf` file for editing. - $ sudo nano /etc/NetworkManager/NetworkManager.conf + $ sudo nano /etc/NetworkManager/NetworkManager.conf 2. Comment out the `dns=dsnmasq` line: - dns=dnsmasq + dns=dnsmasq 3. Save and close the file. 4. Restart both the NetworkManager and Docker. - $ sudo restart network-manager $ sudo restart docker + $ sudo restart network-manager $ sudo restart docker ## Upgrade Docker -To install the latest version of Docker, use the standard `-N` flag with `wget`: +To install the latest version of Docker with `wget`: - $ wget -qO- https://get.docker.com/ | sh + $ wget -qO- https://get.docker.com/ | sh +## Uninstallation + +To uninstall the Docker package: + + $ sudo apt-get purge lxc-docker + +To uninstall the Docker package and dependencies that are no longer needed: + + $ sudo apt-get autoremove --purge lxc-docker + +The above commands will not remove images, containers, volumes, or user created +configuration files on your host. If you wish to delete all images, containers, +and volumes run the following command: + + $ rm -rf /var/lib/docker + +You must delete the user created configuration files manually. diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md index fd3cc7eb4a40a..b5a148417af58 100644 --- a/docs/sources/installation/windows.md +++ b/docs/sources/installation/windows.md @@ -67,7 +67,7 @@ Boot2Docker command requires `ssh.exe` to be in the PATH, therefore we need to include `bin` folder of the Git installation (which has ssh.exe) to the `%PATH%` environment variable by running: - set PATH=%PATH%;"c:\Program Files (x86)\Git\bin" + set PATH=%PATH%;"c:\Program Files (x86)\Git\bin" and then we can run the `boot2docker start` command to start the Boot2Docker VM. (Run `boot2docker init` command if you get an error saying machine does not @@ -81,7 +81,7 @@ to your console window and you are ready to run docker commands such as Launch a PowerShell window, then you need to add `ssh.exe` to your PATH: - $Env:Path = "${Env:Path};c:\Program Files (x86)\Git\bin" + $Env:Path = "${Env:Path};c:\Program Files (x86)\Git\bin" and after running `boot2docker start` command it will print PowerShell commands to set the environment variables to connect Docker running inside VM. Run these @@ -150,6 +150,12 @@ You can do this with - then click: "Save Private Key". - Then use the saved file to login with PuTTY using `docker@127.0.0.1:2022`. +## Uninstallation + +You can uninstall Boot2Docker using Window's standard process for removing programs. +This process does not remove the `docker-install.exe` file. You must delete that file +yourself. + ## References If you have Docker hosts running and if you don't wish to do a From 380b8737523edf1c2575208a14a8673684758692 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 1 May 2015 10:04:24 -0600 Subject: [PATCH 056/321] Only complete repos with "docker pull -a" With this, `docker pull deb` will show all `debian:*` tags, as before, but `docker pull -a deb` will complete directly to just `debian`. :+1: Signed-off-by: Andrew "Tianon" Page --- contrib/completion/bash/docker | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 5b7a102a68396..1aa0835140190 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -679,6 +679,14 @@ _docker_pull() { *) local counter=$(__docker_pos_first_nonflag) if [ $cword -eq $counter ]; then + for arg in "${COMP_WORDS[@]}"; do + case "$arg" in + --all-tags|-a) + __docker_image_repos + return + ;; + esac + done __docker_image_repos_and_tags fi ;; From d9639409fd54ee6955793474fa9a5bac1e583c77 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Fri, 1 May 2015 20:23:44 +0200 Subject: [PATCH 057/321] Provide a struct to configure cli streaming Signed-off-by: Antonio Murdaca --- api/client/build.go | 8 +++++++- api/client/create.go | 7 ++++++- api/client/events.go | 6 +++++- api/client/export.go | 6 +++++- api/client/import.go | 8 +++++++- api/client/load.go | 7 ++++++- api/client/logs.go | 8 +++++++- api/client/save.go | 9 +++++++-- api/client/utils.go | 18 +++++++++++------- 9 files changed, 61 insertions(+), 16 deletions(-) diff --git a/api/client/build.go b/api/client/build.go index e83de976beb18..27b06afb715ba 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -289,7 +289,13 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if context != nil { headers.Set("Content-Type", "application/tar") } - err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers) + sopts := &streamOpts{ + rawTerminal: true, + in: body, + out: cli.out, + headers: headers, + } + err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts) if jerr, ok := err.(*jsonmessage.JSONError); ok { // If no error code is set, default to 1 if jerr.Code == 0 { diff --git a/api/client/create.go b/api/client/create.go index b0819a05d7043..f4d6e376b113a 100644 --- a/api/client/create.go +++ b/api/client/create.go @@ -47,7 +47,12 @@ func (cli *DockerCli) pullImageCustomOut(image string, out io.Writer) error { registryAuthHeader := []string{ base64.URLEncoding.EncodeToString(buf), } - if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, out, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil { + sopts := &streamOpts{ + rawTerminal: true, + out: out, + headers: map[string][]string{"X-Registry-Auth": registryAuthHeader}, + } + if err := cli.stream("POST", "/images/create?"+v.Encode(), sopts); err != nil { return err } return nil diff --git a/api/client/events.go b/api/client/events.go index 2154e0ccd0286..64ec6e139d8ca 100644 --- a/api/client/events.go +++ b/api/client/events.go @@ -63,7 +63,11 @@ func (cli *DockerCli) CmdEvents(args ...string) error { } v.Set("filters", filterJSON) } - if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil { + sopts := &streamOpts{ + rawTerminal: true, + out: cli.out, + } + if err := cli.stream("GET", "/events?"+v.Encode(), sopts); err != nil { return err } return nil diff --git a/api/client/export.go b/api/client/export.go index 1ff46f9b5701d..42b0834739ed6 100644 --- a/api/client/export.go +++ b/api/client/export.go @@ -34,7 +34,11 @@ func (cli *DockerCli) CmdExport(args ...string) error { } image := cmd.Arg(0) - if err := cli.stream("GET", "/containers/"+image+"/export", nil, output, nil); err != nil { + sopts := &streamOpts{ + rawTerminal: true, + out: output, + } + if err := cli.stream("GET", "/containers/"+image+"/export", sopts); err != nil { return err } diff --git a/api/client/import.go b/api/client/import.go index a6cc4cdc7ecc5..48c56896afc16 100644 --- a/api/client/import.go +++ b/api/client/import.go @@ -54,5 +54,11 @@ func (cli *DockerCli) CmdImport(args ...string) error { in = cli.in } - return cli.stream("POST", "/images/create?"+v.Encode(), in, cli.out, nil) + sopts := &streamOpts{ + rawTerminal: true, + in: in, + out: cli.out, + } + + return cli.stream("POST", "/images/create?"+v.Encode(), sopts) } diff --git a/api/client/load.go b/api/client/load.go index 7338c770d65fe..8dd8bb5469738 100644 --- a/api/client/load.go +++ b/api/client/load.go @@ -29,7 +29,12 @@ func (cli *DockerCli) CmdLoad(args ...string) error { return err } } - if err := cli.stream("POST", "/images/load", input, cli.out, nil); err != nil { + sopts := &streamOpts{ + rawTerminal: true, + in: input, + out: cli.out, + } + if err := cli.stream("POST", "/images/load", sopts); err != nil { return err } return nil diff --git a/api/client/logs.go b/api/client/logs.go index 5e5dd9dd8bcb1..171b36da40347 100644 --- a/api/client/logs.go +++ b/api/client/logs.go @@ -52,5 +52,11 @@ func (cli *DockerCli) CmdLogs(args ...string) error { } v.Set("tail", *tail) - return cli.streamHelper("GET", "/containers/"+name+"/logs?"+v.Encode(), c.Config.Tty, nil, cli.out, cli.err, nil) + sopts := &streamOpts{ + rawTerminal: c.Config.Tty, + out: cli.out, + err: cli.err, + } + + return cli.stream("GET", "/containers/"+name+"/logs?"+v.Encode(), sopts) } diff --git a/api/client/save.go b/api/client/save.go index 5d9d276153647..a04cbcf1e985d 100644 --- a/api/client/save.go +++ b/api/client/save.go @@ -34,9 +34,14 @@ func (cli *DockerCli) CmdSave(args ...string) error { return errors.New("Cowardly refusing to save to a terminal. Use the -o flag or redirect.") } + sopts := &streamOpts{ + rawTerminal: true, + out: output, + } + if len(cmd.Args()) == 1 { image := cmd.Arg(0) - if err := cli.stream("GET", "/images/"+image+"/get", nil, output, nil); err != nil { + if err := cli.stream("GET", "/images/"+image+"/get", sopts); err != nil { return err } } else { @@ -44,7 +49,7 @@ func (cli *DockerCli) CmdSave(args ...string) error { for _, arg := range cmd.Args() { v.Add("names", arg) } - if err := cli.stream("GET", "/images/get?"+v.Encode(), nil, output, nil); err != nil { + if err := cli.stream("GET", "/images/get?"+v.Encode(), sopts); err != nil { return err } } diff --git a/api/client/utils.go b/api/client/utils.go index eed1163f838d8..6fb9b256fd7c4 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -170,19 +170,23 @@ func (cli *DockerCli) call(method, path string, data interface{}, headers map[st return body, statusCode, err } -func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error { - return cli.streamHelper(method, path, true, in, out, nil, headers) +type streamOpts struct { + rawTerminal bool + in io.Reader + out io.Writer + err io.Writer + headers map[string][]string } -func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error { - body, contentType, _, err := cli.clientRequest(method, path, in, headers) +func (cli *DockerCli) stream(method, path string, opts *streamOpts) error { + body, contentType, _, err := cli.clientRequest(method, path, opts.in, opts.headers) if err != nil { return err } - return cli.streamBody(body, contentType, setRawTerminal, stdout, stderr) + return cli.streamBody(body, contentType, opts.rawTerminal, opts.out, opts.err) } -func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, setRawTerminal bool, stdout, stderr io.Writer) error { +func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, rawTerminal bool, stdout, stderr io.Writer) error { defer body.Close() if api.MatchesContentType(contentType, "application/json") { @@ -191,7 +195,7 @@ func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, setRawT if stdout != nil || stderr != nil { // When TTY is ON, use regular copy var err error - if setRawTerminal { + if rawTerminal { _, err = io.Copy(stdout, body) } else { _, err = stdcopy.StdCopy(stdout, stderr, body) From a05bcd12c44b4daada51267d89fd9ac53812be02 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Fri, 1 May 2015 13:35:54 -0700 Subject: [PATCH 058/321] Fix race in FirewalldInit It was possible that signalHandler won't start because connections is not assigned. Signed-off-by: Alexander Morozov --- pkg/iptables/firewalld.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/pkg/iptables/firewalld.go b/pkg/iptables/firewalld.go index 3087794131467..1c0cddb0f0c9e 100644 --- a/pkg/iptables/firewalld.go +++ b/pkg/iptables/firewalld.go @@ -2,9 +2,10 @@ package iptables import ( "fmt" + "strings" + "github.com/Sirupsen/logrus" "github.com/godbus/dbus" - "strings" ) type IPV string @@ -40,6 +41,9 @@ func FirewalldInit() { if err != nil { logrus.Errorf("Failed to connect to D-Bus system bus: %s", err) } + if connection != nil { + go signalHandler() + } firewalldRunning = checkRunning() } @@ -76,20 +80,17 @@ func (c *Conn) initConnection() error { c.signal = make(chan *dbus.Signal, 10) c.sysconn.Signal(c.signal) - go signalHandler() return nil } func signalHandler() { - if connection != nil { - for signal := range connection.signal { - if strings.Contains(signal.Name, "NameOwnerChanged") { - firewalldRunning = checkRunning() - dbusConnectionChanged(signal.Body) - } else if strings.Contains(signal.Name, "Reloaded") { - reloaded() - } + for signal := range connection.signal { + if strings.Contains(signal.Name, "NameOwnerChanged") { + firewalldRunning = checkRunning() + dbusConnectionChanged(signal.Body) + } else if strings.Contains(signal.Name, "Reloaded") { + reloaded() } } } From 9f5730e131699caaf6f8f6f941d5942d68295055 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 1 May 2015 14:45:00 -0600 Subject: [PATCH 059/321] Remove hacky "cp .../Dockerfile.build ." in "build-deb" Turns out that `-f` on a file that's in `.dockerignore` actually does work. No idea why it wasn't when I was doing this before, but oh well! :metal: Signed-off-by: Andrew "Tianon" Page --- hack/make/build-deb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hack/make/build-deb b/hack/make/build-deb index a5a6d43870b38..90a2b98b85267 100644 --- a/hack/make/build-deb +++ b/hack/make/build-deb @@ -56,15 +56,11 @@ DEST=$1 RUN { echo '$debSource (${debVersion}-0~${suite}) $suite; urgency=low'; echo; echo ' * Version: $VERSION'; echo; echo " -- $debMaintainer $debDate"; } > debian/changelog && cat >&2 debian/changelog RUN dpkg-buildpackage -uc -us EOF - cp -a "$DEST/$version/Dockerfile.build" . # can't use $DEST because it's in .dockerignore... tempImage="docker-temp/build-deb:$version" - ( set -x && docker build -t "$tempImage" -f Dockerfile.build . ) + ( set -x && docker build -t "$tempImage" -f "$DEST/$version/Dockerfile.build" . ) docker run --rm "$tempImage" bash -c 'cd .. && tar -c *_*' | tar -xvC "$DEST/$version" docker rmi "$tempImage" done - # clean up after ourselves - rm -f Dockerfile.build - source "${MAKEDIR}/.integration-daemon-stop" ) 2>&1 | tee -a "$DEST/test.log" From d317b7c89159f9795fa7eb69504191208b3c0b3f Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 1 May 2015 15:03:08 -0600 Subject: [PATCH 060/321] Add "debian:stretch" as another build-deb target Signed-off-by: Andrew "Tianon" Page --- contrib/builder/deb/debian-stretch/Dockerfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 contrib/builder/deb/debian-stretch/Dockerfile diff --git a/contrib/builder/deb/debian-stretch/Dockerfile b/contrib/builder/deb/debian-stretch/Dockerfile new file mode 100644 index 0000000000000..5bf7536092f19 --- /dev/null +++ b/contrib/builder/deb/debian-stretch/Dockerfile @@ -0,0 +1,14 @@ +# +# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/deb/generate.sh"! +# + +FROM debian:stretch + +RUN apt-get update && apt-get install -y bash-completion btrfs-tools build-essential curl ca-certificates debhelper dh-systemd git libapparmor-dev libdevmapper-dev libsqlite3-dev --no-install-recommends && rm -rf /var/lib/apt/lists/* + +ENV GO_VERSION 1.4.2 +RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xvzC /usr/local +ENV PATH $PATH:/usr/local/go/bin + +ENV AUTO_GOPATH 1 +ENV DOCKER_BUILDTAGS apparmor selinux From 576985a1dcd76a9af2c5c483e6f12035a1f47b96 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 1 May 2015 16:01:10 -0600 Subject: [PATCH 061/321] Finally remove our copy of "archive/tar" now that Go 1.4 is the minimum! IT'S ABOUT TIME. :tada: Signed-off-by: Andrew "Tianon" Page --- graph/tags_unit_test.go | 2 +- hack/vendor.sh | 11 - integration-cli/docker_api_containers_test.go | 2 +- integration-cli/docker_cli_push_test.go | 2 +- integration-cli/utils.go | 2 +- pkg/archive/archive.go | 3 +- pkg/archive/archive_test.go | 2 +- pkg/archive/archive_unix.go | 3 +- pkg/archive/archive_windows.go | 3 +- pkg/archive/changes.go | 3 +- pkg/archive/diff.go | 3 +- pkg/archive/diff_test.go | 3 +- pkg/archive/utils_test.go | 3 +- pkg/archive/wrap.go | 2 +- pkg/tarsum/tarsum.go | 3 +- pkg/tarsum/tarsum_test.go | 3 +- pkg/tarsum/versioning.go | 3 +- .../p/go/src/pkg/archive/tar/common.go | 305 ------- .../p/go/src/pkg/archive/tar/example_test.go | 79 -- .../p/go/src/pkg/archive/tar/reader.go | 820 ------------------ .../p/go/src/pkg/archive/tar/reader_test.go | 743 ---------------- .../p/go/src/pkg/archive/tar/stat_atim.go | 20 - .../go/src/pkg/archive/tar/stat_atimespec.go | 20 - .../p/go/src/pkg/archive/tar/stat_unix.go | 32 - .../p/go/src/pkg/archive/tar/tar_test.go | 284 ------ .../p/go/src/pkg/archive/tar/testdata/gnu.tar | Bin 3072 -> 0 bytes .../src/pkg/archive/tar/testdata/nil-uid.tar | Bin 1024 -> 0 bytes .../p/go/src/pkg/archive/tar/testdata/pax.tar | Bin 10240 -> 0 bytes .../go/src/pkg/archive/tar/testdata/small.txt | 1 - .../src/pkg/archive/tar/testdata/small2.txt | 1 - .../archive/tar/testdata/sparse-formats.tar | Bin 17920 -> 0 bytes .../go/src/pkg/archive/tar/testdata/star.tar | Bin 3072 -> 0 bytes .../go/src/pkg/archive/tar/testdata/ustar.tar | Bin 2048 -> 0 bytes .../p/go/src/pkg/archive/tar/testdata/v7.tar | Bin 3584 -> 0 bytes .../archive/tar/testdata/writer-big-long.tar | Bin 4096 -> 0 bytes .../pkg/archive/tar/testdata/writer-big.tar | Bin 4096 -> 0 bytes .../src/pkg/archive/tar/testdata/writer.tar | Bin 3584 -> 0 bytes .../src/pkg/archive/tar/testdata/xattrs.tar | Bin 5120 -> 0 bytes .../p/go/src/pkg/archive/tar/writer.go | 396 --------- .../p/go/src/pkg/archive/tar/writer_test.go | 491 ----------- 40 files changed, 16 insertions(+), 3229 deletions(-) delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/common.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/example_test.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader_test.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atim.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atimespec.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_unix.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/tar_test.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/gnu.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/nil-uid.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/pax.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small.txt delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small2.txt delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/sparse-formats.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/star.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/ustar.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/v7.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big-long.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/xattrs.tar delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer.go delete mode 100644 vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer_test.go diff --git a/graph/tags_unit_test.go b/graph/tags_unit_test.go index 0482fa58e3caa..db2d32c7fe29e 100644 --- a/graph/tags_unit_test.go +++ b/graph/tags_unit_test.go @@ -1,6 +1,7 @@ package graph import ( + "archive/tar" "bytes" "io" "os" @@ -12,7 +13,6 @@ import ( _ "github.com/docker/docker/daemon/graphdriver/vfs" // import the vfs driver so it is used in the tests "github.com/docker/docker/image" "github.com/docker/docker/utils" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) const ( diff --git a/hack/vendor.sh b/hack/vendor.sh index 8fed05852217f..50cff1690f49e 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -59,17 +59,6 @@ clone git github.com/go-fsnotify/fsnotify v1.0.4 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 -# get Go tip's archive/tar, for xattr support and improved performance -# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep -if [ "$1" = '--go' ]; then - # Go takes forever and a half to clone, so we only redownload it when explicitly requested via the "--go" flag to this script. - clone hg code.google.com/p/go 1b17b3426e3c - mv src/code.google.com/p/go/src/pkg/archive/tar tmp-tar - rm -rf src/code.google.com/p/go - mkdir -p src/code.google.com/p/go/src/pkg/archive - mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar -fi - # get distribution packages clone git github.com/docker/distribution d957768537c5af40e4f4cd96871f7b2bde9e2923 mv src/github.com/docker/distribution/digest tmp-digest diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index e43daed8f0084..2651bd7ac8595 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -1,6 +1,7 @@ package main import ( + "archive/tar" "bytes" "encoding/json" "io" @@ -11,7 +12,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" "github.com/go-check/check" ) diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go index 69a05ed821c78..ca971807f95e5 100644 --- a/integration-cli/docker_cli_push_test.go +++ b/integration-cli/docker_cli_push_test.go @@ -1,6 +1,7 @@ package main import ( + "archive/tar" "fmt" "io/ioutil" "os" @@ -8,7 +9,6 @@ import ( "strings" "time" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" "github.com/go-check/check" ) diff --git a/integration-cli/utils.go b/integration-cli/utils.go index f0de79ea8f071..5fb0b63458e05 100644 --- a/integration-cli/utils.go +++ b/integration-cli/utils.go @@ -1,6 +1,7 @@ package main import ( + "archive/tar" "bytes" "encoding/json" "errors" @@ -17,7 +18,6 @@ import ( "time" "github.com/docker/docker/pkg/stringutils" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) func getExitCode(err error) (int, error) { diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 4d8d260087595..1579df19492cb 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -1,6 +1,7 @@ package archive import ( + "archive/tar" "bufio" "bytes" "compress/bzip2" @@ -16,8 +17,6 @@ import ( "strings" "syscall" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" - "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/pools" diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index ae9b5a8cd2520..f24f628c2f3cc 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -1,6 +1,7 @@ package archive import ( + "archive/tar" "bytes" "fmt" "io" @@ -15,7 +16,6 @@ import ( "time" "github.com/docker/docker/pkg/system" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) func TestIsArchiveNilHeader(t *testing.T) { diff --git a/pkg/archive/archive_unix.go b/pkg/archive/archive_unix.go index 82c9a82c1a5c9..6dc96a4edbbb3 100644 --- a/pkg/archive/archive_unix.go +++ b/pkg/archive/archive_unix.go @@ -3,11 +3,10 @@ package archive import ( + "archive/tar" "errors" "os" "syscall" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) // canonicalTarNameForPath returns platform-specific filepath diff --git a/pkg/archive/archive_windows.go b/pkg/archive/archive_windows.go index 6caef3b735484..593346df2d8f0 100644 --- a/pkg/archive/archive_windows.go +++ b/pkg/archive/archive_windows.go @@ -3,11 +3,10 @@ package archive import ( + "archive/tar" "fmt" "os" "strings" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) // canonicalTarNameForPath returns platform-specific filepath diff --git a/pkg/archive/changes.go b/pkg/archive/changes.go index 06fad8eb4f77f..d03af0fed6bde 100644 --- a/pkg/archive/changes.go +++ b/pkg/archive/changes.go @@ -1,6 +1,7 @@ package archive import ( + "archive/tar" "bytes" "fmt" "io" @@ -11,8 +12,6 @@ import ( "syscall" "time" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" - "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/system" diff --git a/pkg/archive/diff.go b/pkg/archive/diff.go index b5eb63fd44254..a8314bc1f3f00 100644 --- a/pkg/archive/diff.go +++ b/pkg/archive/diff.go @@ -1,6 +1,7 @@ package archive import ( + "archive/tar" "fmt" "io" "io/ioutil" @@ -9,8 +10,6 @@ import ( "strings" "syscall" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" - "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/system" ) diff --git a/pkg/archive/diff_test.go b/pkg/archive/diff_test.go index 758c4115d5732..01ed4372805ba 100644 --- a/pkg/archive/diff_test.go +++ b/pkg/archive/diff_test.go @@ -1,9 +1,8 @@ package archive import ( + "archive/tar" "testing" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) func TestApplyLayerInvalidFilenames(t *testing.T) { diff --git a/pkg/archive/utils_test.go b/pkg/archive/utils_test.go index 9048027203a50..2a266c2fdfd09 100644 --- a/pkg/archive/utils_test.go +++ b/pkg/archive/utils_test.go @@ -1,6 +1,7 @@ package archive import ( + "archive/tar" "bytes" "fmt" "io" @@ -8,8 +9,6 @@ import ( "os" "path/filepath" "time" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) var testUntarFns = map[string]func(string, io.Reader) error{ diff --git a/pkg/archive/wrap.go b/pkg/archive/wrap.go index b8b60197a3d83..dfb335c0b6c04 100644 --- a/pkg/archive/wrap.go +++ b/pkg/archive/wrap.go @@ -1,8 +1,8 @@ package archive import ( + "archive/tar" "bytes" - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" "io/ioutil" ) diff --git a/pkg/tarsum/tarsum.go b/pkg/tarsum/tarsum.go index 88fcbe4a946b9..a778bb0b9dabb 100644 --- a/pkg/tarsum/tarsum.go +++ b/pkg/tarsum/tarsum.go @@ -1,6 +1,7 @@ package tarsum import ( + "archive/tar" "bytes" "compress/gzip" "crypto" @@ -11,8 +12,6 @@ import ( "hash" "io" "strings" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) const ( diff --git a/pkg/tarsum/tarsum_test.go b/pkg/tarsum/tarsum_test.go index 26f12cc847367..968d7c7cf16f3 100644 --- a/pkg/tarsum/tarsum_test.go +++ b/pkg/tarsum/tarsum_test.go @@ -1,6 +1,7 @@ package tarsum import ( + "archive/tar" "bytes" "compress/gzip" "crypto/md5" @@ -14,8 +15,6 @@ import ( "io/ioutil" "os" "testing" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) type testLayer struct { diff --git a/pkg/tarsum/versioning.go b/pkg/tarsum/versioning.go index 0ceb5298a35fb..3cdc6ddaa4421 100644 --- a/pkg/tarsum/versioning.go +++ b/pkg/tarsum/versioning.go @@ -1,12 +1,11 @@ package tarsum import ( + "archive/tar" "errors" "sort" "strconv" "strings" - - "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" ) // versioning of the TarSum algorithm diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/common.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/common.go deleted file mode 100644 index e363aa793e0f3..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/common.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package tar implements access to tar archives. -// It aims to cover most of the variations, including those produced -// by GNU and BSD tars. -// -// References: -// http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5 -// http://www.gnu.org/software/tar/manual/html_node/Standard.html -// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html -package tar - -import ( - "bytes" - "errors" - "fmt" - "os" - "path" - "time" -) - -const ( - blockSize = 512 - - // Types - TypeReg = '0' // regular file - TypeRegA = '\x00' // regular file - TypeLink = '1' // hard link - TypeSymlink = '2' // symbolic link - TypeChar = '3' // character device node - TypeBlock = '4' // block device node - TypeDir = '5' // directory - TypeFifo = '6' // fifo node - TypeCont = '7' // reserved - TypeXHeader = 'x' // extended header - TypeXGlobalHeader = 'g' // global extended header - TypeGNULongName = 'L' // Next file has a long name - TypeGNULongLink = 'K' // Next file symlinks to a file w/ a long name - TypeGNUSparse = 'S' // sparse file -) - -// A Header represents a single header in a tar archive. -// Some fields may not be populated. -type Header struct { - Name string // name of header file entry - Mode int64 // permission and mode bits - Uid int // user id of owner - Gid int // group id of owner - Size int64 // length in bytes - ModTime time.Time // modified time - Typeflag byte // type of header entry - Linkname string // target name of link - Uname string // user name of owner - Gname string // group name of owner - Devmajor int64 // major number of character or block device - Devminor int64 // minor number of character or block device - AccessTime time.Time // access time - ChangeTime time.Time // status change time - Xattrs map[string]string -} - -// File name constants from the tar spec. -const ( - fileNameSize = 100 // Maximum number of bytes in a standard tar name. - fileNamePrefixSize = 155 // Maximum number of ustar extension bytes. -) - -// FileInfo returns an os.FileInfo for the Header. -func (h *Header) FileInfo() os.FileInfo { - return headerFileInfo{h} -} - -// headerFileInfo implements os.FileInfo. -type headerFileInfo struct { - h *Header -} - -func (fi headerFileInfo) Size() int64 { return fi.h.Size } -func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } -func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } -func (fi headerFileInfo) Sys() interface{} { return fi.h } - -// Name returns the base name of the file. -func (fi headerFileInfo) Name() string { - if fi.IsDir() { - return path.Base(path.Clean(fi.h.Name)) - } - return path.Base(fi.h.Name) -} - -// Mode returns the permission and mode bits for the headerFileInfo. -func (fi headerFileInfo) Mode() (mode os.FileMode) { - // Set file permission bits. - mode = os.FileMode(fi.h.Mode).Perm() - - // Set setuid, setgid and sticky bits. - if fi.h.Mode&c_ISUID != 0 { - // setuid - mode |= os.ModeSetuid - } - if fi.h.Mode&c_ISGID != 0 { - // setgid - mode |= os.ModeSetgid - } - if fi.h.Mode&c_ISVTX != 0 { - // sticky - mode |= os.ModeSticky - } - - // Set file mode bits. - // clear perm, setuid, setgid and sticky bits. - m := os.FileMode(fi.h.Mode) &^ 07777 - if m == c_ISDIR { - // directory - mode |= os.ModeDir - } - if m == c_ISFIFO { - // named pipe (FIFO) - mode |= os.ModeNamedPipe - } - if m == c_ISLNK { - // symbolic link - mode |= os.ModeSymlink - } - if m == c_ISBLK { - // device file - mode |= os.ModeDevice - } - if m == c_ISCHR { - // Unix character device - mode |= os.ModeDevice - mode |= os.ModeCharDevice - } - if m == c_ISSOCK { - // Unix domain socket - mode |= os.ModeSocket - } - - switch fi.h.Typeflag { - case TypeLink, TypeSymlink: - // hard link, symbolic link - mode |= os.ModeSymlink - case TypeChar: - // character device node - mode |= os.ModeDevice - mode |= os.ModeCharDevice - case TypeBlock: - // block device node - mode |= os.ModeDevice - case TypeDir: - // directory - mode |= os.ModeDir - case TypeFifo: - // fifo node - mode |= os.ModeNamedPipe - } - - return mode -} - -// sysStat, if non-nil, populates h from system-dependent fields of fi. -var sysStat func(fi os.FileInfo, h *Header) error - -// Mode constants from the tar spec. -const ( - c_ISUID = 04000 // Set uid - c_ISGID = 02000 // Set gid - c_ISVTX = 01000 // Save text (sticky bit) - c_ISDIR = 040000 // Directory - c_ISFIFO = 010000 // FIFO - c_ISREG = 0100000 // Regular file - c_ISLNK = 0120000 // Symbolic link - c_ISBLK = 060000 // Block special file - c_ISCHR = 020000 // Character special file - c_ISSOCK = 0140000 // Socket -) - -// Keywords for the PAX Extended Header -const ( - paxAtime = "atime" - paxCharset = "charset" - paxComment = "comment" - paxCtime = "ctime" // please note that ctime is not a valid pax header. - paxGid = "gid" - paxGname = "gname" - paxLinkpath = "linkpath" - paxMtime = "mtime" - paxPath = "path" - paxSize = "size" - paxUid = "uid" - paxUname = "uname" - paxXattr = "SCHILY.xattr." - paxNone = "" -) - -// FileInfoHeader creates a partially-populated Header from fi. -// If fi describes a symlink, FileInfoHeader records link as the link target. -// If fi describes a directory, a slash is appended to the name. -// Because os.FileInfo's Name method returns only the base name of -// the file it describes, it may be necessary to modify the Name field -// of the returned header to provide the full path name of the file. -func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { - if fi == nil { - return nil, errors.New("tar: FileInfo is nil") - } - fm := fi.Mode() - h := &Header{ - Name: fi.Name(), - ModTime: fi.ModTime(), - Mode: int64(fm.Perm()), // or'd with c_IS* constants later - } - switch { - case fm.IsRegular(): - h.Mode |= c_ISREG - h.Typeflag = TypeReg - h.Size = fi.Size() - case fi.IsDir(): - h.Typeflag = TypeDir - h.Mode |= c_ISDIR - h.Name += "/" - case fm&os.ModeSymlink != 0: - h.Typeflag = TypeSymlink - h.Mode |= c_ISLNK - h.Linkname = link - case fm&os.ModeDevice != 0: - if fm&os.ModeCharDevice != 0 { - h.Mode |= c_ISCHR - h.Typeflag = TypeChar - } else { - h.Mode |= c_ISBLK - h.Typeflag = TypeBlock - } - case fm&os.ModeNamedPipe != 0: - h.Typeflag = TypeFifo - h.Mode |= c_ISFIFO - case fm&os.ModeSocket != 0: - h.Mode |= c_ISSOCK - default: - return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) - } - if fm&os.ModeSetuid != 0 { - h.Mode |= c_ISUID - } - if fm&os.ModeSetgid != 0 { - h.Mode |= c_ISGID - } - if fm&os.ModeSticky != 0 { - h.Mode |= c_ISVTX - } - if sysStat != nil { - return h, sysStat(fi, h) - } - return h, nil -} - -var zeroBlock = make([]byte, blockSize) - -// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values. -// We compute and return both. -func checksum(header []byte) (unsigned int64, signed int64) { - for i := 0; i < len(header); i++ { - if i == 148 { - // The chksum field (header[148:156]) is special: it should be treated as space bytes. - unsigned += ' ' * 8 - signed += ' ' * 8 - i += 7 - continue - } - unsigned += int64(header[i]) - signed += int64(int8(header[i])) - } - return -} - -type slicer []byte - -func (sp *slicer) next(n int) (b []byte) { - s := *sp - b, *sp = s[0:n], s[n:] - return -} - -func isASCII(s string) bool { - for _, c := range s { - if c >= 0x80 { - return false - } - } - return true -} - -func toASCII(s string) string { - if isASCII(s) { - return s - } - var buf bytes.Buffer - for _, c := range s { - if c < 0x80 { - buf.WriteByte(byte(c)) - } - } - return buf.String() -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/example_test.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/example_test.go deleted file mode 100644 index 351eaa0e6cc94..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/example_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar_test - -import ( - "archive/tar" - "bytes" - "fmt" - "io" - "log" - "os" -) - -func Example() { - // Create a buffer to write our archive to. - buf := new(bytes.Buffer) - - // Create a new tar archive. - tw := tar.NewWriter(buf) - - // Add some files to the archive. - var files = []struct { - Name, Body string - }{ - {"readme.txt", "This archive contains some text files."}, - {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, - {"todo.txt", "Get animal handling licence."}, - } - for _, file := range files { - hdr := &tar.Header{ - Name: file.Name, - Size: int64(len(file.Body)), - } - if err := tw.WriteHeader(hdr); err != nil { - log.Fatalln(err) - } - if _, err := tw.Write([]byte(file.Body)); err != nil { - log.Fatalln(err) - } - } - // Make sure to check the error on Close. - if err := tw.Close(); err != nil { - log.Fatalln(err) - } - - // Open the tar archive for reading. - r := bytes.NewReader(buf.Bytes()) - tr := tar.NewReader(r) - - // Iterate through the files in the archive. - for { - hdr, err := tr.Next() - if err == io.EOF { - // end of tar archive - break - } - if err != nil { - log.Fatalln(err) - } - fmt.Printf("Contents of %s:\n", hdr.Name) - if _, err := io.Copy(os.Stdout, tr); err != nil { - log.Fatalln(err) - } - fmt.Println() - } - - // Output: - // Contents of readme.txt: - // This archive contains some text files. - // Contents of gopher.txt: - // Gopher names: - // George - // Geoffrey - // Gonzo - // Contents of todo.txt: - // Get animal handling licence. -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader.go deleted file mode 100644 index a27559d0f042c..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader.go +++ /dev/null @@ -1,820 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -// TODO(dsymonds): -// - pax extensions - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "os" - "strconv" - "strings" - "time" -) - -var ( - ErrHeader = errors.New("archive/tar: invalid tar header") -) - -const maxNanoSecondIntSize = 9 - -// A Reader provides sequential access to the contents of a tar archive. -// A tar archive consists of a sequence of files. -// The Next method advances to the next file in the archive (including the first), -// and then it can be treated as an io.Reader to access the file's data. -type Reader struct { - r io.Reader - err error - pad int64 // amount of padding (ignored) after current file entry - curr numBytesReader // reader for current file entry - hdrBuff [blockSize]byte // buffer to use in readHeader -} - -// A numBytesReader is an io.Reader with a numBytes method, returning the number -// of bytes remaining in the underlying encoded data. -type numBytesReader interface { - io.Reader - numBytes() int64 -} - -// A regFileReader is a numBytesReader for reading file data from a tar archive. -type regFileReader struct { - r io.Reader // underlying reader - nb int64 // number of unread bytes for current file entry -} - -// A sparseFileReader is a numBytesReader for reading sparse file data from a tar archive. -type sparseFileReader struct { - rfr *regFileReader // reads the sparse-encoded file data - sp []sparseEntry // the sparse map for the file - pos int64 // keeps track of file position - tot int64 // total size of the file -} - -// Keywords for GNU sparse files in a PAX extended header -const ( - paxGNUSparseNumBlocks = "GNU.sparse.numblocks" - paxGNUSparseOffset = "GNU.sparse.offset" - paxGNUSparseNumBytes = "GNU.sparse.numbytes" - paxGNUSparseMap = "GNU.sparse.map" - paxGNUSparseName = "GNU.sparse.name" - paxGNUSparseMajor = "GNU.sparse.major" - paxGNUSparseMinor = "GNU.sparse.minor" - paxGNUSparseSize = "GNU.sparse.size" - paxGNUSparseRealSize = "GNU.sparse.realsize" -) - -// Keywords for old GNU sparse headers -const ( - oldGNUSparseMainHeaderOffset = 386 - oldGNUSparseMainHeaderIsExtendedOffset = 482 - oldGNUSparseMainHeaderNumEntries = 4 - oldGNUSparseExtendedHeaderIsExtendedOffset = 504 - oldGNUSparseExtendedHeaderNumEntries = 21 - oldGNUSparseOffsetSize = 12 - oldGNUSparseNumBytesSize = 12 -) - -// NewReader creates a new Reader reading from r. -func NewReader(r io.Reader) *Reader { return &Reader{r: r} } - -// Next advances to the next entry in the tar archive. -func (tr *Reader) Next() (*Header, error) { - var hdr *Header - if tr.err == nil { - tr.skipUnread() - } - if tr.err != nil { - return hdr, tr.err - } - hdr = tr.readHeader() - if hdr == nil { - return hdr, tr.err - } - // Check for PAX/GNU header. - switch hdr.Typeflag { - case TypeXHeader: - // PAX extended header - headers, err := parsePAX(tr) - if err != nil { - return nil, err - } - // We actually read the whole file, - // but this skips alignment padding - tr.skipUnread() - hdr = tr.readHeader() - mergePAX(hdr, headers) - - // Check for a PAX format sparse file - sp, err := tr.checkForGNUSparsePAXHeaders(hdr, headers) - if err != nil { - tr.err = err - return nil, err - } - if sp != nil { - // Current file is a PAX format GNU sparse file. - // Set the current file reader to a sparse file reader. - tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size} - } - return hdr, nil - case TypeGNULongName: - // We have a GNU long name header. Its contents are the real file name. - realname, err := ioutil.ReadAll(tr) - if err != nil { - return nil, err - } - hdr, err := tr.Next() - hdr.Name = cString(realname) - return hdr, err - case TypeGNULongLink: - // We have a GNU long link header. - realname, err := ioutil.ReadAll(tr) - if err != nil { - return nil, err - } - hdr, err := tr.Next() - hdr.Linkname = cString(realname) - return hdr, err - } - return hdr, tr.err -} - -// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then -// this function reads the sparse map and returns it. Unknown sparse formats are ignored, causing the file to -// be treated as a regular file. -func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]string) ([]sparseEntry, error) { - var sparseFormat string - - // Check for sparse format indicators - major, majorOk := headers[paxGNUSparseMajor] - minor, minorOk := headers[paxGNUSparseMinor] - sparseName, sparseNameOk := headers[paxGNUSparseName] - _, sparseMapOk := headers[paxGNUSparseMap] - sparseSize, sparseSizeOk := headers[paxGNUSparseSize] - sparseRealSize, sparseRealSizeOk := headers[paxGNUSparseRealSize] - - // Identify which, if any, sparse format applies from which PAX headers are set - if majorOk && minorOk { - sparseFormat = major + "." + minor - } else if sparseNameOk && sparseMapOk { - sparseFormat = "0.1" - } else if sparseSizeOk { - sparseFormat = "0.0" - } else { - // Not a PAX format GNU sparse file. - return nil, nil - } - - // Check for unknown sparse format - if sparseFormat != "0.0" && sparseFormat != "0.1" && sparseFormat != "1.0" { - return nil, nil - } - - // Update hdr from GNU sparse PAX headers - if sparseNameOk { - hdr.Name = sparseName - } - if sparseSizeOk { - realSize, err := strconv.ParseInt(sparseSize, 10, 0) - if err != nil { - return nil, ErrHeader - } - hdr.Size = realSize - } else if sparseRealSizeOk { - realSize, err := strconv.ParseInt(sparseRealSize, 10, 0) - if err != nil { - return nil, ErrHeader - } - hdr.Size = realSize - } - - // Set up the sparse map, according to the particular sparse format in use - var sp []sparseEntry - var err error - switch sparseFormat { - case "0.0", "0.1": - sp, err = readGNUSparseMap0x1(headers) - case "1.0": - sp, err = readGNUSparseMap1x0(tr.curr) - } - return sp, err -} - -// mergePAX merges well known headers according to PAX standard. -// In general headers with the same name as those found -// in the header struct overwrite those found in the header -// struct with higher precision or longer values. Esp. useful -// for name and linkname fields. -func mergePAX(hdr *Header, headers map[string]string) error { - for k, v := range headers { - switch k { - case paxPath: - hdr.Name = v - case paxLinkpath: - hdr.Linkname = v - case paxGname: - hdr.Gname = v - case paxUname: - hdr.Uname = v - case paxUid: - uid, err := strconv.ParseInt(v, 10, 0) - if err != nil { - return err - } - hdr.Uid = int(uid) - case paxGid: - gid, err := strconv.ParseInt(v, 10, 0) - if err != nil { - return err - } - hdr.Gid = int(gid) - case paxAtime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.AccessTime = t - case paxMtime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.ModTime = t - case paxCtime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.ChangeTime = t - case paxSize: - size, err := strconv.ParseInt(v, 10, 0) - if err != nil { - return err - } - hdr.Size = int64(size) - default: - if strings.HasPrefix(k, paxXattr) { - if hdr.Xattrs == nil { - hdr.Xattrs = make(map[string]string) - } - hdr.Xattrs[k[len(paxXattr):]] = v - } - } - } - return nil -} - -// parsePAXTime takes a string of the form %d.%d as described in -// the PAX specification. -func parsePAXTime(t string) (time.Time, error) { - buf := []byte(t) - pos := bytes.IndexByte(buf, '.') - var seconds, nanoseconds int64 - var err error - if pos == -1 { - seconds, err = strconv.ParseInt(t, 10, 0) - if err != nil { - return time.Time{}, err - } - } else { - seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0) - if err != nil { - return time.Time{}, err - } - nano_buf := string(buf[pos+1:]) - // Pad as needed before converting to a decimal. - // For example .030 -> .030000000 -> 30000000 nanoseconds - if len(nano_buf) < maxNanoSecondIntSize { - // Right pad - nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf)) - } else if len(nano_buf) > maxNanoSecondIntSize { - // Right truncate - nano_buf = nano_buf[:maxNanoSecondIntSize] - } - nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0) - if err != nil { - return time.Time{}, err - } - } - ts := time.Unix(seconds, nanoseconds) - return ts, nil -} - -// parsePAX parses PAX headers. -// If an extended header (type 'x') is invalid, ErrHeader is returned -func parsePAX(r io.Reader) (map[string]string, error) { - buf, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - // For GNU PAX sparse format 0.0 support. - // This function transforms the sparse format 0.0 headers into sparse format 0.1 headers. - var sparseMap bytes.Buffer - - headers := make(map[string]string) - // Each record is constructed as - // "%d %s=%s\n", length, keyword, value - for len(buf) > 0 { - // or the header was empty to start with. - var sp int - // The size field ends at the first space. - sp = bytes.IndexByte(buf, ' ') - if sp == -1 { - return nil, ErrHeader - } - // Parse the first token as a decimal integer. - n, err := strconv.ParseInt(string(buf[:sp]), 10, 0) - if err != nil { - return nil, ErrHeader - } - // Extract everything between the decimal and the n -1 on the - // beginning to eat the ' ', -1 on the end to skip the newline. - var record []byte - record, buf = buf[sp+1:n-1], buf[n:] - // The first equals is guaranteed to mark the end of the key. - // Everything else is value. - eq := bytes.IndexByte(record, '=') - if eq == -1 { - return nil, ErrHeader - } - key, value := record[:eq], record[eq+1:] - - keyStr := string(key) - if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes { - // GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map. - sparseMap.Write(value) - sparseMap.Write([]byte{','}) - } else { - // Normal key. Set the value in the headers map. - headers[keyStr] = string(value) - } - } - if sparseMap.Len() != 0 { - // Add sparse info to headers, chopping off the extra comma - sparseMap.Truncate(sparseMap.Len() - 1) - headers[paxGNUSparseMap] = sparseMap.String() - } - return headers, nil -} - -// cString parses bytes as a NUL-terminated C-style string. -// If a NUL byte is not found then the whole slice is returned as a string. -func cString(b []byte) string { - n := 0 - for n < len(b) && b[n] != 0 { - n++ - } - return string(b[0:n]) -} - -func (tr *Reader) octal(b []byte) int64 { - // Check for binary format first. - if len(b) > 0 && b[0]&0x80 != 0 { - var x int64 - for i, c := range b { - if i == 0 { - c &= 0x7f // ignore signal bit in first byte - } - x = x<<8 | int64(c) - } - return x - } - - // Because unused fields are filled with NULs, we need - // to skip leading NULs. Fields may also be padded with - // spaces or NULs. - // So we remove leading and trailing NULs and spaces to - // be sure. - b = bytes.Trim(b, " \x00") - - if len(b) == 0 { - return 0 - } - x, err := strconv.ParseUint(cString(b), 8, 64) - if err != nil { - tr.err = err - } - return int64(x) -} - -// skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding. -func (tr *Reader) skipUnread() { - nr := tr.numBytes() + tr.pad // number of bytes to skip - tr.curr, tr.pad = nil, 0 - if sr, ok := tr.r.(io.Seeker); ok { - if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil { - return - } - } - _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr) -} - -func (tr *Reader) verifyChecksum(header []byte) bool { - if tr.err != nil { - return false - } - - given := tr.octal(header[148:156]) - unsigned, signed := checksum(header) - return given == unsigned || given == signed -} - -func (tr *Reader) readHeader() *Header { - header := tr.hdrBuff[:] - copy(header, zeroBlock) - - if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { - return nil - } - - // Two blocks of zero bytes marks the end of the archive. - if bytes.Equal(header, zeroBlock[0:blockSize]) { - if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { - return nil - } - if bytes.Equal(header, zeroBlock[0:blockSize]) { - tr.err = io.EOF - } else { - tr.err = ErrHeader // zero block and then non-zero block - } - return nil - } - - if !tr.verifyChecksum(header) { - tr.err = ErrHeader - return nil - } - - // Unpack - hdr := new(Header) - s := slicer(header) - - hdr.Name = cString(s.next(100)) - hdr.Mode = tr.octal(s.next(8)) - hdr.Uid = int(tr.octal(s.next(8))) - hdr.Gid = int(tr.octal(s.next(8))) - hdr.Size = tr.octal(s.next(12)) - hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) - s.next(8) // chksum - hdr.Typeflag = s.next(1)[0] - hdr.Linkname = cString(s.next(100)) - - // The remainder of the header depends on the value of magic. - // The original (v7) version of tar had no explicit magic field, - // so its magic bytes, like the rest of the block, are NULs. - magic := string(s.next(8)) // contains version field as well. - var format string - switch { - case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988) - if string(header[508:512]) == "tar\x00" { - format = "star" - } else { - format = "posix" - } - case magic == "ustar \x00": // old GNU tar - format = "gnu" - } - - switch format { - case "posix", "gnu", "star": - hdr.Uname = cString(s.next(32)) - hdr.Gname = cString(s.next(32)) - devmajor := s.next(8) - devminor := s.next(8) - if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock { - hdr.Devmajor = tr.octal(devmajor) - hdr.Devminor = tr.octal(devminor) - } - var prefix string - switch format { - case "posix", "gnu": - prefix = cString(s.next(155)) - case "star": - prefix = cString(s.next(131)) - hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0) - hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0) - } - if len(prefix) > 0 { - hdr.Name = prefix + "/" + hdr.Name - } - } - - if tr.err != nil { - tr.err = ErrHeader - return nil - } - - // Maximum value of hdr.Size is 64 GB (12 octal digits), - // so there's no risk of int64 overflowing. - nb := int64(hdr.Size) - tr.pad = -nb & (blockSize - 1) // blockSize is a power of two - - // Set the current file reader. - tr.curr = ®FileReader{r: tr.r, nb: nb} - - // Check for old GNU sparse format entry. - if hdr.Typeflag == TypeGNUSparse { - // Get the real size of the file. - hdr.Size = tr.octal(header[483:495]) - - // Read the sparse map. - sp := tr.readOldGNUSparseMap(header) - if tr.err != nil { - return nil - } - // Current file is a GNU sparse file. Update the current file reader. - tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size} - } - - return hdr -} - -// A sparseEntry holds a single entry in a sparse file's sparse map. -// A sparse entry indicates the offset and size in a sparse file of a -// block of data. -type sparseEntry struct { - offset int64 - numBytes int64 -} - -// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format. -// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries, -// then one or more extension headers are used to store the rest of the sparse map. -func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry { - isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0 - spCap := oldGNUSparseMainHeaderNumEntries - if isExtended { - spCap += oldGNUSparseExtendedHeaderNumEntries - } - sp := make([]sparseEntry, 0, spCap) - s := slicer(header[oldGNUSparseMainHeaderOffset:]) - - // Read the four entries from the main tar header - for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ { - offset := tr.octal(s.next(oldGNUSparseOffsetSize)) - numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize)) - if tr.err != nil { - tr.err = ErrHeader - return nil - } - if offset == 0 && numBytes == 0 { - break - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - - for isExtended { - // There are more entries. Read an extension header and parse its entries. - sparseHeader := make([]byte, blockSize) - if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil { - return nil - } - isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0 - s = slicer(sparseHeader) - for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ { - offset := tr.octal(s.next(oldGNUSparseOffsetSize)) - numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize)) - if tr.err != nil { - tr.err = ErrHeader - return nil - } - if offset == 0 && numBytes == 0 { - break - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - } - return sp -} - -// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format version 1.0. -// The sparse map is stored just before the file data and padded out to the nearest block boundary. -func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) { - buf := make([]byte, 2*blockSize) - sparseHeader := buf[:blockSize] - - // readDecimal is a helper function to read a decimal integer from the sparse map - // while making sure to read from the file in blocks of size blockSize - readDecimal := func() (int64, error) { - // Look for newline - nl := bytes.IndexByte(sparseHeader, '\n') - if nl == -1 { - if len(sparseHeader) >= blockSize { - // This is an error - return 0, ErrHeader - } - oldLen := len(sparseHeader) - newLen := oldLen + blockSize - if cap(sparseHeader) < newLen { - // There's more header, but we need to make room for the next block - copy(buf, sparseHeader) - sparseHeader = buf[:newLen] - } else { - // There's more header, and we can just reslice - sparseHeader = sparseHeader[:newLen] - } - - // Now that sparseHeader is large enough, read next block - if _, err := io.ReadFull(r, sparseHeader[oldLen:newLen]); err != nil { - return 0, err - } - - // Look for a newline in the new data - nl = bytes.IndexByte(sparseHeader[oldLen:newLen], '\n') - if nl == -1 { - // This is an error - return 0, ErrHeader - } - nl += oldLen // We want the position from the beginning - } - // Now that we've found a newline, read a number - n, err := strconv.ParseInt(string(sparseHeader[:nl]), 10, 0) - if err != nil { - return 0, ErrHeader - } - - // Update sparseHeader to consume this number - sparseHeader = sparseHeader[nl+1:] - return n, nil - } - - // Read the first block - if _, err := io.ReadFull(r, sparseHeader); err != nil { - return nil, err - } - - // The first line contains the number of entries - numEntries, err := readDecimal() - if err != nil { - return nil, err - } - - // Read all the entries - sp := make([]sparseEntry, 0, numEntries) - for i := int64(0); i < numEntries; i++ { - // Read the offset - offset, err := readDecimal() - if err != nil { - return nil, err - } - // Read numBytes - numBytes, err := readDecimal() - if err != nil { - return nil, err - } - - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - - return sp, nil -} - -// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format version 0.1. -// The sparse map is stored in the PAX headers. -func readGNUSparseMap0x1(headers map[string]string) ([]sparseEntry, error) { - // Get number of entries - numEntriesStr, ok := headers[paxGNUSparseNumBlocks] - if !ok { - return nil, ErrHeader - } - numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) - if err != nil { - return nil, ErrHeader - } - - sparseMap := strings.Split(headers[paxGNUSparseMap], ",") - - // There should be two numbers in sparseMap for each entry - if int64(len(sparseMap)) != 2*numEntries { - return nil, ErrHeader - } - - // Loop through the entries in the sparse map - sp := make([]sparseEntry, 0, numEntries) - for i := int64(0); i < numEntries; i++ { - offset, err := strconv.ParseInt(sparseMap[2*i], 10, 0) - if err != nil { - return nil, ErrHeader - } - numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 0) - if err != nil { - return nil, ErrHeader - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - - return sp, nil -} - -// numBytes returns the number of bytes left to read in the current file's entry -// in the tar archive, or 0 if there is no current file. -func (tr *Reader) numBytes() int64 { - if tr.curr == nil { - // No current file, so no bytes - return 0 - } - return tr.curr.numBytes() -} - -// Read reads from the current entry in the tar archive. -// It returns 0, io.EOF when it reaches the end of that entry, -// until Next is called to advance to the next entry. -func (tr *Reader) Read(b []byte) (n int, err error) { - if tr.curr == nil { - return 0, io.EOF - } - n, err = tr.curr.Read(b) - if err != nil && err != io.EOF { - tr.err = err - } - return -} - -func (rfr *regFileReader) Read(b []byte) (n int, err error) { - if rfr.nb == 0 { - // file consumed - return 0, io.EOF - } - if int64(len(b)) > rfr.nb { - b = b[0:rfr.nb] - } - n, err = rfr.r.Read(b) - rfr.nb -= int64(n) - - if err == io.EOF && rfr.nb > 0 { - err = io.ErrUnexpectedEOF - } - return -} - -// numBytes returns the number of bytes left to read in the file's data in the tar archive. -func (rfr *regFileReader) numBytes() int64 { - return rfr.nb -} - -// readHole reads a sparse file hole ending at offset toOffset -func (sfr *sparseFileReader) readHole(b []byte, toOffset int64) int { - n64 := toOffset - sfr.pos - if n64 > int64(len(b)) { - n64 = int64(len(b)) - } - n := int(n64) - for i := 0; i < n; i++ { - b[i] = 0 - } - sfr.pos += n64 - return n -} - -// Read reads the sparse file data in expanded form. -func (sfr *sparseFileReader) Read(b []byte) (n int, err error) { - if len(sfr.sp) == 0 { - // No more data fragments to read from. - if sfr.pos < sfr.tot { - // We're in the last hole - n = sfr.readHole(b, sfr.tot) - return - } - // Otherwise, we're at the end of the file - return 0, io.EOF - } - if sfr.pos < sfr.sp[0].offset { - // We're in a hole - n = sfr.readHole(b, sfr.sp[0].offset) - return - } - - // We're not in a hole, so we'll read from the next data fragment - posInFragment := sfr.pos - sfr.sp[0].offset - bytesLeft := sfr.sp[0].numBytes - posInFragment - if int64(len(b)) > bytesLeft { - b = b[0:bytesLeft] - } - - n, err = sfr.rfr.Read(b) - sfr.pos += int64(n) - - if int64(n) == bytesLeft { - // We're done with this fragment - sfr.sp = sfr.sp[1:] - } - - if err == io.EOF && sfr.pos < sfr.tot { - // We reached the end of the last fragment's data, but there's a final hole - err = nil - } - return -} - -// numBytes returns the number of bytes left to read in the sparse file's -// sparse-encoded data in the tar archive. -func (sfr *sparseFileReader) numBytes() int64 { - return sfr.rfr.nb -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader_test.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader_test.go deleted file mode 100644 index 9601ffe459767..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/reader_test.go +++ /dev/null @@ -1,743 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -import ( - "bytes" - "crypto/md5" - "fmt" - "io" - "io/ioutil" - "os" - "reflect" - "strings" - "testing" - "time" -) - -type untarTest struct { - file string - headers []*Header - cksums []string -} - -var gnuTarTest = &untarTest{ - file: "testdata/gnu.tar", - headers: []*Header{ - { - Name: "small.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 5, - ModTime: time.Unix(1244428340, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - }, - { - Name: "small2.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 11, - ModTime: time.Unix(1244436044, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - }, - }, - cksums: []string{ - "e38b27eaccb4391bdec553a7f3ae6b2f", - "c65bd2e50a56a2138bf1716f2fd56fe9", - }, -} - -var sparseTarTest = &untarTest{ - file: "testdata/sparse-formats.tar", - headers: []*Header{ - { - Name: "sparse-gnu", - Mode: 420, - Uid: 1000, - Gid: 1000, - Size: 200, - ModTime: time.Unix(1392395740, 0), - Typeflag: 0x53, - Linkname: "", - Uname: "david", - Gname: "david", - Devmajor: 0, - Devminor: 0, - }, - { - Name: "sparse-posix-0.0", - Mode: 420, - Uid: 1000, - Gid: 1000, - Size: 200, - ModTime: time.Unix(1392342187, 0), - Typeflag: 0x30, - Linkname: "", - Uname: "david", - Gname: "david", - Devmajor: 0, - Devminor: 0, - }, - { - Name: "sparse-posix-0.1", - Mode: 420, - Uid: 1000, - Gid: 1000, - Size: 200, - ModTime: time.Unix(1392340456, 0), - Typeflag: 0x30, - Linkname: "", - Uname: "david", - Gname: "david", - Devmajor: 0, - Devminor: 0, - }, - { - Name: "sparse-posix-1.0", - Mode: 420, - Uid: 1000, - Gid: 1000, - Size: 200, - ModTime: time.Unix(1392337404, 0), - Typeflag: 0x30, - Linkname: "", - Uname: "david", - Gname: "david", - Devmajor: 0, - Devminor: 0, - }, - { - Name: "end", - Mode: 420, - Uid: 1000, - Gid: 1000, - Size: 4, - ModTime: time.Unix(1392398319, 0), - Typeflag: 0x30, - Linkname: "", - Uname: "david", - Gname: "david", - Devmajor: 0, - Devminor: 0, - }, - }, - cksums: []string{ - "6f53234398c2449fe67c1812d993012f", - "6f53234398c2449fe67c1812d993012f", - "6f53234398c2449fe67c1812d993012f", - "6f53234398c2449fe67c1812d993012f", - "b0061974914468de549a2af8ced10316", - }, -} - -var untarTests = []*untarTest{ - gnuTarTest, - sparseTarTest, - { - file: "testdata/star.tar", - headers: []*Header{ - { - Name: "small.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 5, - ModTime: time.Unix(1244592783, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - AccessTime: time.Unix(1244592783, 0), - ChangeTime: time.Unix(1244592783, 0), - }, - { - Name: "small2.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 11, - ModTime: time.Unix(1244592783, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - AccessTime: time.Unix(1244592783, 0), - ChangeTime: time.Unix(1244592783, 0), - }, - }, - }, - { - file: "testdata/v7.tar", - headers: []*Header{ - { - Name: "small.txt", - Mode: 0444, - Uid: 73025, - Gid: 5000, - Size: 5, - ModTime: time.Unix(1244593104, 0), - Typeflag: '\x00', - }, - { - Name: "small2.txt", - Mode: 0444, - Uid: 73025, - Gid: 5000, - Size: 11, - ModTime: time.Unix(1244593104, 0), - Typeflag: '\x00', - }, - }, - }, - { - file: "testdata/pax.tar", - headers: []*Header{ - { - Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", - Mode: 0664, - Uid: 1000, - Gid: 1000, - Uname: "shane", - Gname: "shane", - Size: 7, - ModTime: time.Unix(1350244992, 23960108), - ChangeTime: time.Unix(1350244992, 23960108), - AccessTime: time.Unix(1350244992, 23960108), - Typeflag: TypeReg, - }, - { - Name: "a/b", - Mode: 0777, - Uid: 1000, - Gid: 1000, - Uname: "shane", - Gname: "shane", - Size: 0, - ModTime: time.Unix(1350266320, 910238425), - ChangeTime: time.Unix(1350266320, 910238425), - AccessTime: time.Unix(1350266320, 910238425), - Typeflag: TypeSymlink, - Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", - }, - }, - }, - { - file: "testdata/nil-uid.tar", // golang.org/issue/5290 - headers: []*Header{ - { - Name: "P1050238.JPG.log", - Mode: 0664, - Uid: 0, - Gid: 0, - Size: 14, - ModTime: time.Unix(1365454838, 0), - Typeflag: TypeReg, - Linkname: "", - Uname: "eyefi", - Gname: "eyefi", - Devmajor: 0, - Devminor: 0, - }, - }, - }, - { - file: "testdata/xattrs.tar", - headers: []*Header{ - { - Name: "small.txt", - Mode: 0644, - Uid: 1000, - Gid: 10, - Size: 5, - ModTime: time.Unix(1386065770, 448252320), - Typeflag: '0', - Uname: "alex", - Gname: "wheel", - AccessTime: time.Unix(1389782991, 419875220), - ChangeTime: time.Unix(1389782956, 794414986), - Xattrs: map[string]string{ - "user.key": "value", - "user.key2": "value2", - // Interestingly, selinux encodes the terminating null inside the xattr - "security.selinux": "unconfined_u:object_r:default_t:s0\x00", - }, - }, - { - Name: "small2.txt", - Mode: 0644, - Uid: 1000, - Gid: 10, - Size: 11, - ModTime: time.Unix(1386065770, 449252304), - Typeflag: '0', - Uname: "alex", - Gname: "wheel", - AccessTime: time.Unix(1389782991, 419875220), - ChangeTime: time.Unix(1386065770, 449252304), - Xattrs: map[string]string{ - "security.selinux": "unconfined_u:object_r:default_t:s0\x00", - }, - }, - }, - }, -} - -func TestReader(t *testing.T) { -testLoop: - for i, test := range untarTests { - f, err := os.Open(test.file) - if err != nil { - t.Errorf("test %d: Unexpected error: %v", i, err) - continue - } - defer f.Close() - tr := NewReader(f) - for j, header := range test.headers { - hdr, err := tr.Next() - if err != nil || hdr == nil { - t.Errorf("test %d, entry %d: Didn't get entry: %v", i, j, err) - f.Close() - continue testLoop - } - if !reflect.DeepEqual(*hdr, *header) { - t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", - i, j, *hdr, *header) - } - } - hdr, err := tr.Next() - if err == io.EOF { - continue testLoop - } - if hdr != nil || err != nil { - t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) - } - } -} - -func TestPartialRead(t *testing.T) { - f, err := os.Open("testdata/gnu.tar") - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - defer f.Close() - - tr := NewReader(f) - - // Read the first four bytes; Next() should skip the last byte. - hdr, err := tr.Next() - if err != nil || hdr == nil { - t.Fatalf("Didn't get first file: %v", err) - } - buf := make([]byte, 4) - if _, err := io.ReadFull(tr, buf); err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if expected := []byte("Kilt"); !bytes.Equal(buf, expected) { - t.Errorf("Contents = %v, want %v", buf, expected) - } - - // Second file - hdr, err = tr.Next() - if err != nil || hdr == nil { - t.Fatalf("Didn't get second file: %v", err) - } - buf = make([]byte, 6) - if _, err := io.ReadFull(tr, buf); err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if expected := []byte("Google"); !bytes.Equal(buf, expected) { - t.Errorf("Contents = %v, want %v", buf, expected) - } -} - -func TestIncrementalRead(t *testing.T) { - test := gnuTarTest - f, err := os.Open(test.file) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - defer f.Close() - - tr := NewReader(f) - - headers := test.headers - cksums := test.cksums - nread := 0 - - // loop over all files - for ; ; nread++ { - hdr, err := tr.Next() - if hdr == nil || err == io.EOF { - break - } - - // check the header - if !reflect.DeepEqual(*hdr, *headers[nread]) { - t.Errorf("Incorrect header:\nhave %+v\nwant %+v", - *hdr, headers[nread]) - } - - // read file contents in little chunks EOF, - // checksumming all the way - h := md5.New() - rdbuf := make([]uint8, 8) - for { - nr, err := tr.Read(rdbuf) - if err == io.EOF { - break - } - if err != nil { - t.Errorf("Read: unexpected error %v\n", err) - break - } - h.Write(rdbuf[0:nr]) - } - // verify checksum - have := fmt.Sprintf("%x", h.Sum(nil)) - want := cksums[nread] - if want != have { - t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) - } - } - if nread != len(headers) { - t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread) - } -} - -func TestNonSeekable(t *testing.T) { - test := gnuTarTest - f, err := os.Open(test.file) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - defer f.Close() - - type readerOnly struct { - io.Reader - } - tr := NewReader(readerOnly{f}) - nread := 0 - - for ; ; nread++ { - _, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - } - - if nread != len(test.headers) { - t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(test.headers), nread) - } -} - -func TestParsePAXHeader(t *testing.T) { - paxTests := [][3]string{ - {"a", "a=name", "10 a=name\n"}, // Test case involving multiple acceptable lengths - {"a", "a=name", "9 a=name\n"}, // Test case involving multiple acceptable length - {"mtime", "mtime=1350244992.023960108", "30 mtime=1350244992.023960108\n"}} - for _, test := range paxTests { - key, expected, raw := test[0], test[1], test[2] - reader := bytes.NewReader([]byte(raw)) - headers, err := parsePAX(reader) - if err != nil { - t.Errorf("Couldn't parse correctly formatted headers: %v", err) - continue - } - if strings.EqualFold(headers[key], expected) { - t.Errorf("mtime header incorrectly parsed: got %s, wanted %s", headers[key], expected) - continue - } - trailer := make([]byte, 100) - n, err := reader.Read(trailer) - if err != io.EOF || n != 0 { - t.Error("Buffer wasn't consumed") - } - } - badHeader := bytes.NewReader([]byte("3 somelongkey=")) - if _, err := parsePAX(badHeader); err != ErrHeader { - t.Fatal("Unexpected success when parsing bad header") - } -} - -func TestParsePAXTime(t *testing.T) { - // Some valid PAX time values - timestamps := map[string]time.Time{ - "1350244992.023960108": time.Unix(1350244992, 23960108), // The common case - "1350244992.02396010": time.Unix(1350244992, 23960100), // Lower precision value - "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value - "1350244992": time.Unix(1350244992, 0), // Low precision value - } - for input, expected := range timestamps { - ts, err := parsePAXTime(input) - if err != nil { - t.Fatal(err) - } - if !ts.Equal(expected) { - t.Fatalf("Time parsing failure %s %s", ts, expected) - } - } -} - -func TestMergePAX(t *testing.T) { - hdr := new(Header) - // Test a string, integer, and time based value. - headers := map[string]string{ - "path": "a/b/c", - "uid": "1000", - "mtime": "1350244992.023960108", - } - err := mergePAX(hdr, headers) - if err != nil { - t.Fatal(err) - } - want := &Header{ - Name: "a/b/c", - Uid: 1000, - ModTime: time.Unix(1350244992, 23960108), - } - if !reflect.DeepEqual(hdr, want) { - t.Errorf("incorrect merge: got %+v, want %+v", hdr, want) - } -} - -func TestSparseEndToEnd(t *testing.T) { - test := sparseTarTest - f, err := os.Open(test.file) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - defer f.Close() - - tr := NewReader(f) - - headers := test.headers - cksums := test.cksums - nread := 0 - - // loop over all files - for ; ; nread++ { - hdr, err := tr.Next() - if hdr == nil || err == io.EOF { - break - } - - // check the header - if !reflect.DeepEqual(*hdr, *headers[nread]) { - t.Errorf("Incorrect header:\nhave %+v\nwant %+v", - *hdr, headers[nread]) - } - - // read and checksum the file data - h := md5.New() - _, err = io.Copy(h, tr) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - // verify checksum - have := fmt.Sprintf("%x", h.Sum(nil)) - want := cksums[nread] - if want != have { - t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want) - } - } - if nread != len(headers) { - t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread) - } -} - -type sparseFileReadTest struct { - sparseData []byte - sparseMap []sparseEntry - realSize int64 - expected []byte -} - -var sparseFileReadTests = []sparseFileReadTest{ - { - sparseData: []byte("abcde"), - sparseMap: []sparseEntry{ - {offset: 0, numBytes: 2}, - {offset: 5, numBytes: 3}, - }, - realSize: 8, - expected: []byte("ab\x00\x00\x00cde"), - }, - { - sparseData: []byte("abcde"), - sparseMap: []sparseEntry{ - {offset: 0, numBytes: 2}, - {offset: 5, numBytes: 3}, - }, - realSize: 10, - expected: []byte("ab\x00\x00\x00cde\x00\x00"), - }, - { - sparseData: []byte("abcde"), - sparseMap: []sparseEntry{ - {offset: 1, numBytes: 3}, - {offset: 6, numBytes: 2}, - }, - realSize: 8, - expected: []byte("\x00abc\x00\x00de"), - }, - { - sparseData: []byte("abcde"), - sparseMap: []sparseEntry{ - {offset: 1, numBytes: 3}, - {offset: 6, numBytes: 2}, - }, - realSize: 10, - expected: []byte("\x00abc\x00\x00de\x00\x00"), - }, - { - sparseData: []byte(""), - sparseMap: nil, - realSize: 2, - expected: []byte("\x00\x00"), - }, -} - -func TestSparseFileReader(t *testing.T) { - for i, test := range sparseFileReadTests { - r := bytes.NewReader(test.sparseData) - nb := int64(r.Len()) - sfr := &sparseFileReader{ - rfr: ®FileReader{r: r, nb: nb}, - sp: test.sparseMap, - pos: 0, - tot: test.realSize, - } - if sfr.numBytes() != nb { - t.Errorf("test %d: Before reading, sfr.numBytes() = %d, want %d", i, sfr.numBytes(), nb) - } - buf, err := ioutil.ReadAll(sfr) - if err != nil { - t.Errorf("test %d: Unexpected error: %v", i, err) - } - if e := test.expected; !bytes.Equal(buf, e) { - t.Errorf("test %d: Contents = %v, want %v", i, buf, e) - } - if sfr.numBytes() != 0 { - t.Errorf("test %d: After draining the reader, numBytes() was nonzero", i) - } - } -} - -func TestSparseIncrementalRead(t *testing.T) { - sparseMap := []sparseEntry{{10, 2}} - sparseData := []byte("Go") - expected := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Go\x00\x00\x00\x00\x00\x00\x00\x00" - - r := bytes.NewReader(sparseData) - nb := int64(r.Len()) - sfr := &sparseFileReader{ - rfr: ®FileReader{r: r, nb: nb}, - sp: sparseMap, - pos: 0, - tot: int64(len(expected)), - } - - // We'll read the data 6 bytes at a time, with a hole of size 10 at - // the beginning and one of size 8 at the end. - var outputBuf bytes.Buffer - buf := make([]byte, 6) - for { - n, err := sfr.Read(buf) - if err == io.EOF { - break - } - if err != nil { - t.Errorf("Read: unexpected error %v\n", err) - } - if n > 0 { - _, err := outputBuf.Write(buf[:n]) - if err != nil { - t.Errorf("Write: unexpected error %v\n", err) - } - } - } - got := outputBuf.String() - if got != expected { - t.Errorf("Contents = %v, want %v", got, expected) - } -} - -func TestReadGNUSparseMap0x1(t *testing.T) { - headers := map[string]string{ - paxGNUSparseNumBlocks: "4", - paxGNUSparseMap: "0,5,10,5,20,5,30,5", - } - expected := []sparseEntry{ - {offset: 0, numBytes: 5}, - {offset: 10, numBytes: 5}, - {offset: 20, numBytes: 5}, - {offset: 30, numBytes: 5}, - } - - sp, err := readGNUSparseMap0x1(headers) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if !reflect.DeepEqual(sp, expected) { - t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected) - } -} - -func TestReadGNUSparseMap1x0(t *testing.T) { - // This test uses lots of holes so the sparse header takes up more than two blocks - numEntries := 100 - expected := make([]sparseEntry, 0, numEntries) - sparseMap := new(bytes.Buffer) - - fmt.Fprintf(sparseMap, "%d\n", numEntries) - for i := 0; i < numEntries; i++ { - offset := int64(2048 * i) - numBytes := int64(1024) - expected = append(expected, sparseEntry{offset: offset, numBytes: numBytes}) - fmt.Fprintf(sparseMap, "%d\n%d\n", offset, numBytes) - } - - // Make the header the smallest multiple of blockSize that fits the sparseMap - headerBlocks := (sparseMap.Len() + blockSize - 1) / blockSize - bufLen := blockSize * headerBlocks - buf := make([]byte, bufLen) - copy(buf, sparseMap.Bytes()) - - // Get an reader to read the sparse map - r := bytes.NewReader(buf) - - // Read the sparse map - sp, err := readGNUSparseMap1x0(r) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if !reflect.DeepEqual(sp, expected) { - t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected) - } -} - -func TestUninitializedRead(t *testing.T) { - test := gnuTarTest - f, err := os.Open(test.file) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - defer f.Close() - - tr := NewReader(f) - _, err = tr.Read([]byte{}) - if err == nil || err != io.EOF { - t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF) - } - -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atim.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atim.go deleted file mode 100644 index cf9cc79c5915b..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atim.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux dragonfly openbsd solaris - -package tar - -import ( - "syscall" - "time" -) - -func statAtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Atim.Unix()) -} - -func statCtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Ctim.Unix()) -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atimespec.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atimespec.go deleted file mode 100644 index 6f17dbe30725c..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_atimespec.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd netbsd - -package tar - -import ( - "syscall" - "time" -) - -func statAtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Atimespec.Unix()) -} - -func statCtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Ctimespec.Unix()) -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_unix.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_unix.go deleted file mode 100644 index cb843db4cfd65..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/stat_unix.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux darwin dragonfly freebsd openbsd netbsd solaris - -package tar - -import ( - "os" - "syscall" -) - -func init() { - sysStat = statUnix -} - -func statUnix(fi os.FileInfo, h *Header) error { - sys, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return nil - } - h.Uid = int(sys.Uid) - h.Gid = int(sys.Gid) - // TODO(bradfitz): populate username & group. os/user - // doesn't cache LookupId lookups, and lacks group - // lookup functions. - h.AccessTime = statAtime(sys) - h.ChangeTime = statCtime(sys) - // TODO(bradfitz): major/minor device numbers? - return nil -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/tar_test.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/tar_test.go deleted file mode 100644 index ed333f3ea4f14..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/tar_test.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -import ( - "bytes" - "io/ioutil" - "os" - "path" - "reflect" - "strings" - "testing" - "time" -) - -func TestFileInfoHeader(t *testing.T) { - fi, err := os.Stat("testdata/small.txt") - if err != nil { - t.Fatal(err) - } - h, err := FileInfoHeader(fi, "") - if err != nil { - t.Fatalf("FileInfoHeader: %v", err) - } - if g, e := h.Name, "small.txt"; g != e { - t.Errorf("Name = %q; want %q", g, e) - } - if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e { - t.Errorf("Mode = %#o; want %#o", g, e) - } - if g, e := h.Size, int64(5); g != e { - t.Errorf("Size = %v; want %v", g, e) - } - if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { - t.Errorf("ModTime = %v; want %v", g, e) - } - // FileInfoHeader should error when passing nil FileInfo - if _, err := FileInfoHeader(nil, ""); err == nil { - t.Fatalf("Expected error when passing nil to FileInfoHeader") - } -} - -func TestFileInfoHeaderDir(t *testing.T) { - fi, err := os.Stat("testdata") - if err != nil { - t.Fatal(err) - } - h, err := FileInfoHeader(fi, "") - if err != nil { - t.Fatalf("FileInfoHeader: %v", err) - } - if g, e := h.Name, "testdata/"; g != e { - t.Errorf("Name = %q; want %q", g, e) - } - // Ignoring c_ISGID for golang.org/issue/4867 - if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e { - t.Errorf("Mode = %#o; want %#o", g, e) - } - if g, e := h.Size, int64(0); g != e { - t.Errorf("Size = %v; want %v", g, e) - } - if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { - t.Errorf("ModTime = %v; want %v", g, e) - } -} - -func TestFileInfoHeaderSymlink(t *testing.T) { - h, err := FileInfoHeader(symlink{}, "some-target") - if err != nil { - t.Fatal(err) - } - if g, e := h.Name, "some-symlink"; g != e { - t.Errorf("Name = %q; want %q", g, e) - } - if g, e := h.Linkname, "some-target"; g != e { - t.Errorf("Linkname = %q; want %q", g, e) - } -} - -type symlink struct{} - -func (symlink) Name() string { return "some-symlink" } -func (symlink) Size() int64 { return 0 } -func (symlink) Mode() os.FileMode { return os.ModeSymlink } -func (symlink) ModTime() time.Time { return time.Time{} } -func (symlink) IsDir() bool { return false } -func (symlink) Sys() interface{} { return nil } - -func TestRoundTrip(t *testing.T) { - data := []byte("some file contents") - - var b bytes.Buffer - tw := NewWriter(&b) - hdr := &Header{ - Name: "file.txt", - Uid: 1 << 21, // too big for 8 octal digits - Size: int64(len(data)), - ModTime: time.Now(), - } - // tar only supports second precision. - hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond) - if err := tw.WriteHeader(hdr); err != nil { - t.Fatalf("tw.WriteHeader: %v", err) - } - if _, err := tw.Write(data); err != nil { - t.Fatalf("tw.Write: %v", err) - } - if err := tw.Close(); err != nil { - t.Fatalf("tw.Close: %v", err) - } - - // Read it back. - tr := NewReader(&b) - rHdr, err := tr.Next() - if err != nil { - t.Fatalf("tr.Next: %v", err) - } - if !reflect.DeepEqual(rHdr, hdr) { - t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr) - } - rData, err := ioutil.ReadAll(tr) - if err != nil { - t.Fatalf("Read: %v", err) - } - if !bytes.Equal(rData, data) { - t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data) - } -} - -type headerRoundTripTest struct { - h *Header - fm os.FileMode -} - -func TestHeaderRoundTrip(t *testing.T) { - golden := []headerRoundTripTest{ - // regular file. - { - h: &Header{ - Name: "test.txt", - Mode: 0644 | c_ISREG, - Size: 12, - ModTime: time.Unix(1360600916, 0), - Typeflag: TypeReg, - }, - fm: 0644, - }, - // hard link. - { - h: &Header{ - Name: "hard.txt", - Mode: 0644 | c_ISLNK, - Size: 0, - ModTime: time.Unix(1360600916, 0), - Typeflag: TypeLink, - }, - fm: 0644 | os.ModeSymlink, - }, - // symbolic link. - { - h: &Header{ - Name: "link.txt", - Mode: 0777 | c_ISLNK, - Size: 0, - ModTime: time.Unix(1360600852, 0), - Typeflag: TypeSymlink, - }, - fm: 0777 | os.ModeSymlink, - }, - // character device node. - { - h: &Header{ - Name: "dev/null", - Mode: 0666 | c_ISCHR, - Size: 0, - ModTime: time.Unix(1360578951, 0), - Typeflag: TypeChar, - }, - fm: 0666 | os.ModeDevice | os.ModeCharDevice, - }, - // block device node. - { - h: &Header{ - Name: "dev/sda", - Mode: 0660 | c_ISBLK, - Size: 0, - ModTime: time.Unix(1360578954, 0), - Typeflag: TypeBlock, - }, - fm: 0660 | os.ModeDevice, - }, - // directory. - { - h: &Header{ - Name: "dir/", - Mode: 0755 | c_ISDIR, - Size: 0, - ModTime: time.Unix(1360601116, 0), - Typeflag: TypeDir, - }, - fm: 0755 | os.ModeDir, - }, - // fifo node. - { - h: &Header{ - Name: "dev/initctl", - Mode: 0600 | c_ISFIFO, - Size: 0, - ModTime: time.Unix(1360578949, 0), - Typeflag: TypeFifo, - }, - fm: 0600 | os.ModeNamedPipe, - }, - // setuid. - { - h: &Header{ - Name: "bin/su", - Mode: 0755 | c_ISREG | c_ISUID, - Size: 23232, - ModTime: time.Unix(1355405093, 0), - Typeflag: TypeReg, - }, - fm: 0755 | os.ModeSetuid, - }, - // setguid. - { - h: &Header{ - Name: "group.txt", - Mode: 0750 | c_ISREG | c_ISGID, - Size: 0, - ModTime: time.Unix(1360602346, 0), - Typeflag: TypeReg, - }, - fm: 0750 | os.ModeSetgid, - }, - // sticky. - { - h: &Header{ - Name: "sticky.txt", - Mode: 0600 | c_ISREG | c_ISVTX, - Size: 7, - ModTime: time.Unix(1360602540, 0), - Typeflag: TypeReg, - }, - fm: 0600 | os.ModeSticky, - }, - } - - for i, g := range golden { - fi := g.h.FileInfo() - h2, err := FileInfoHeader(fi, "") - if err != nil { - t.Error(err) - continue - } - if strings.Contains(fi.Name(), "/") { - t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name()) - } - name := path.Base(g.h.Name) - if fi.IsDir() { - name += "/" - } - if got, want := h2.Name, name; got != want { - t.Errorf("i=%d: Name: got %v, want %v", i, got, want) - } - if got, want := h2.Size, g.h.Size; got != want { - t.Errorf("i=%d: Size: got %v, want %v", i, got, want) - } - if got, want := h2.Mode, g.h.Mode; got != want { - t.Errorf("i=%d: Mode: got %o, want %o", i, got, want) - } - if got, want := fi.Mode(), g.fm; got != want { - t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want) - } - if got, want := h2.ModTime, g.h.ModTime; got != want { - t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want) - } - if sysh, ok := fi.Sys().(*Header); !ok || sysh != g.h { - t.Errorf("i=%d: Sys didn't return original *Header", i) - } - } -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/gnu.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/gnu.tar deleted file mode 100644 index fc899dc8dc2ad9952f5c5f67a0c76ca2d87249e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmeHH%L>9U5Ztq0(Jv@FdDKtv;8zq|ijXv5BIw^6DOh^2UK)_HbK1>@VQ0c5`qsHR zJrb1zXEcV16&lMRW}rdtKd=NSXg->JkvP|Esp4`g&CK_h+FMmo7oR?iU7RP&svn2t z!9Ke4)upeR_aRYKtT+(g`B!B>fS>t?p7IZ9V9LKWlK+)w+iY|SVQ_tY3I4Ddrx1w) M;($0H4*b6ZFWOBnBLDyZ diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/nil-uid.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/nil-uid.tar deleted file mode 100644 index cc9cfaa33cc5de0a28b4183c1705d801f788c96a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmWGAG%z(VGPcn33UJrU$xmmX0WbgpGcywmlR@HOU}(l*Xk=(?U}j`)Zf3?{U78BOoLqCLtvwr=Z5b$i&RT%Er#Y zO+ZjcSVUAHn~8MUp(~vBV+cg7LjpEKCCE6D7E@EwTcMv_>l+&bbg`j1Cv0A776ym5t@+ zSt9MDBFtXbKY&m4pMN0f`l~hhD>#q(-`x$5n+q@eEPmAevA;0XTM8XMYkTvSmQ-t5 zkihVw{(qQ#_JjT})&KMa&-FhG0c8or{CPvw|Jf69WL!B2Wa1KoKYcMW6^2fg(@@ia-%40!5$*6oDd81d2cr_`3;w E2V3|JA^-pY diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small.txt b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small.txt deleted file mode 100644 index b249bfc518a8c..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small.txt +++ /dev/null @@ -1 +0,0 @@ -Kilts \ No newline at end of file diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small2.txt b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small2.txt deleted file mode 100644 index 394ee3ecd0edf..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/small2.txt +++ /dev/null @@ -1 +0,0 @@ -Google.com diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/sparse-formats.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/sparse-formats.tar deleted file mode 100644 index 8bd4e74d50f9c8961f80a887ab7d6449e032048b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17920 zcmeHO!BXQ!5M{6a3g^xmb&sU64qQV{sZ?#{1DvdPiv%!*Aw}}_dEEjdi|Nre#)hpG zE{}(KnjXC;wbY|&t*;k1>*dFY-EbwpT`b+-m>u zXfjaoe5W44g1VMFbuvaLV|3acePf?HHj7T34f|}^XTyHz*zDR5hW%jJ$GN!K=dPX7 zuwNSXOT&I?*sl!xm0`a!>{o{UdfWbohf`t0wKm47jd5yYoVY#C#(p&HN5g(h+o$d^ z>C~x6+ovLJp9;gi;Rj^+0U3Tkh98jO2W0pG8Gb;9ACTb()boS>@h8I{`Aj1#H@B=dZfDAvtjWMmW;RoC~7Py0M z`m*5%-1CF}@n^#y*zgB7{DBRBV8b8S@CP>hfen8^_^{DnOAo^z5MmhHr;h_0e!zww zu;B-6_yHS!z=j{N;RkH^0r&ji+3`30fen9P!ynl22R8fx0blw!^!(v@`{T)$#0AMUzUr{%bWEKK}dPBZfAtotM&Q)$6}V4GkAAL?ydIxj}Q`3 zJOATY>UD~$8khO$y?3COY_Ib_T!Mz?cSHC~#(oEVI84ue{e9LR^x69SzvU?x#e`$G z`ReZSkBilxf3HuQYO>v9_2tWYd3#C|uKGRxyy zk#CN0vO|t>vO|t?vO|t@vV)g2dr7mGGDo)W_L8o>q-!tf+DkfmNk=c~=p`M!q@$Pg+)H}y zB|Z0&o_k5py`&p2>BdW1A|f+1N!@lEFX<*ndTZ#%;H1d0PWQ;sPWQ<1PWQ+WPxo*$ zCpU9)GbcB5ax*74^K5XIR5u%)rF*!UXXCT<7;fg-2rW5AHbhJJa5K*aY3VWC%(G!y za*S-8mhRzZo{iMfW4M`TW3}WM*$8{;Fcc4%{&{rCCA9dZs{Iw=Go{iJw}H4J9rlMBkscMKka?4V*dGW zC;x{dmiMq8gvD(vpG{xk(ev}2>9_pg&wuy1`g67#*MIt_+k5+eaQ%mN-{S%QM+!~( zxc)<2*YN+U4#l|sv%B)c7PePszGeL<)ZO)vtHtH=w09GsNfox9d|WQBPwAMB1HKi$ z5#I)1l17qNl4g>25`gi0%mT0gEC34-1PB5I0fGQQfKq@`fKq@`fKq@;fJ%T$fJ%T$ zfLefBfLefBfLeekKolSf5Cw<=%mtVWFc)Ahz+8YvfJT5ufJT5u0A#CaDG)Nzv=opE zMO*%@0IdS81gZg+MP*A>0a;*L*S;zQ^1P%)r9keM))iGXNaa8-mb9xN$g|SAj;op= zlS*1t6=X?iT~QSVc~H`#(jdo4>x!y6$YPQf)rV9dQiVt*BGrggBvO?~WSR`0jpG)F zR$z95<=;=bg;QIfR|BZ{k=7%FW59w-S{C9wpVT}I{Ao4pNVj%vb z{pbH+{)aiAzW>2BZdt7HACK|hLCzZHZZvnf_-l0|IXl~}=T~SgCPR@QPL^Kc(9Lpj zv56@U!e<=Br@-+2fA>qk!2KVorji&Q8CK+X>e0g#)6 z@dUuK3}6apYu09*vX znm!5vu=b8Z0L;v^6bQklmI7jCCS}XN6`)n1l|VJX%uKdX6)-c?y7pBeFf)@Dl>##} ztt+Z(U}h#Qst0CfT31vh!CNoVqM~4CrgcSC7r2GAs4|$DXJmbGf$=ejIaDU{qjK;EfgdABYgg9smFU diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/star.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/star.tar deleted file mode 100644 index 59e2d4e604611eeac3e2a0f3d6f71d2623c50449..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmeHHT?)e>4DRzz;R#BjQ;)ERouag*479>@u-$$NbG3!-WyoMlUh?xvNIv}HZD&jy zuA!-C5KZlY0Y@bP833Zfm_JQ2M2yBQ2dTK6K{>VDLBV=D{z>IvVF` zUD#xgUGh?F1AikeIW6NnOIrMRGU4UU`62nAWxyx>^STEhN#m{lQEc_E}GZPaA5N&Q|3Z@N=AbgM*P?o{a$k4#V#K6eR)QrKv#MIE( zgh9c8hHiozU0Pg{SOj!ZaYkZZDqIwk0aTWjhA9jefq29K;yD8YhMfGo^t{B}RQ&;E gz@3MSk&&8{lh1`qc2s;c1V%$(Gz3ONV7P_=0FTf|dgec!sXT^AK{$jKc@-kWdUe(&uh-&e0L+xARj zwLzm>LI~3|1sT#R&XkBIzWbfCPrYEK7fr^Q@7vXO;&pw$QCTT3-?&yO+jq(<{6qS`FS_vP zIBhMBjnmsnS~{|C9LMN8#r!W{zj5l&zcE?^U_t*||1zJ{zqInH{-Zy}2$O|c?WSFx zxn8RtM3-UpAJiW`Z@Zar#$ojz)NjtWBfnULUzD=jj5!>iG>O2k{o(=ZAg=$-urC7q zVm{n!{kK`S@p|Vk`q%aFg#nw)bMB-40yAj*%7=F37m@ziFINBH7pTSD@Cfil^^9T6 zxL-iu+Aq)#ev#CF(l2&S@A^eC<`;^e4{ZQ#s9$Y4r}$iP3;;e3V;a&MNN*s$f%FFc H(;N5+1FUK9 diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big-long.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer-big-long.tar deleted file mode 100644 index 5960ee824784ffeacb976a9c648be41b0281508b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeIuJqp7x3tu-|!r}ytVByrmfae ipO37m$1T~NWs?FFpa2CZKmiI+fC3bt00k&;vcMnFf)<_t diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/writer.tar deleted file mode 100644 index e6d816ad0775d56d09242d6f5d1dbe56af310a32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3584 zcmeHIK@P$o5ajGDd_l9j6nKIMUt!cVjT92WM1L@81h#LhDgML6Bon)c?rO_kPgyt^3D0fH9$GJM`O*&4VCw= zv#H)UKC-TtzNwGuV$*%C{bm zsdIMLR{C5VZL^vBE!S4cfUeCYt@>GOiAt%sq7tp|_iN{x5cDreh9ME=K+wOCQm`$x j!znSk-v6Dy)}|V_!f*AilYjI7l|Jj-R%ReG@B;%+QQ}au diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/xattrs.tar b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/testdata/xattrs.tar deleted file mode 100644 index 9701950edd1f0dc82858b7117136b37391be0b08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5120 zcmeHJv2KGf5M|~o_yWg1+khiw>d;i}P^nX=$R$ooYd`|ilD{uBAv6g^kxC>6-(uu< zHg^v_-l5r}td>fyRbC(vfcdOQq}Iq(#u+Ja9X?}Dv(|CCVoJF~09ZgF;2a!G7^%~| zYNYoMUQ-rE=5KzzBJ^EKyr-Mx-NQ4gq%k=v3zee}wOxElT`HH-ei(K*xV|_} zC{$GDvDuoW?o>&odUrVuVHkt_w?IH zW3PV_@V!Jxt@A^i>Yrj(>;K=H?5X8!tJS~MYVd#a^`?|QJKb&Uduf~MfN4M7$J!Lr zF40zZMF!9x{tqJ#0F5+;{2!=)=Knre|G(mAKU`hAc#r>!#{V(9d;sW1hxVv7@B_zF ze)#eKF~#1~>@WTI`#+&4`lkel_5U6!N8h^5vRAE8lqGgr9-Ul!p=H1_U>TS&1K)l2 B)fNB% diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer.go deleted file mode 100644 index dafb2cabf37a8..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer.go +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -// TODO(dsymonds): -// - catch more errors (no first header, etc.) - -import ( - "bytes" - "errors" - "fmt" - "io" - "os" - "path" - "strconv" - "strings" - "time" -) - -var ( - ErrWriteTooLong = errors.New("archive/tar: write too long") - ErrFieldTooLong = errors.New("archive/tar: header field too long") - ErrWriteAfterClose = errors.New("archive/tar: write after close") - errNameTooLong = errors.New("archive/tar: name too long") - errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values") -) - -// A Writer provides sequential writing of a tar archive in POSIX.1 format. -// A tar archive consists of a sequence of files. -// Call WriteHeader to begin a new file, and then call Write to supply that file's data, -// writing at most hdr.Size bytes in total. -type Writer struct { - w io.Writer - err error - nb int64 // number of unwritten bytes for current file entry - pad int64 // amount of padding to write after current file entry - closed bool - usedBinary bool // whether the binary numeric field extension was used - preferPax bool // use pax header instead of binary numeric header - hdrBuff [blockSize]byte // buffer to use in writeHeader when writing a regular header - paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header -} - -// NewWriter creates a new Writer writing to w. -func NewWriter(w io.Writer) *Writer { return &Writer{w: w} } - -// Flush finishes writing the current file (optional). -func (tw *Writer) Flush() error { - if tw.nb > 0 { - tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb) - return tw.err - } - - n := tw.nb + tw.pad - for n > 0 && tw.err == nil { - nr := n - if nr > blockSize { - nr = blockSize - } - var nw int - nw, tw.err = tw.w.Write(zeroBlock[0:nr]) - n -= int64(nw) - } - tw.nb = 0 - tw.pad = 0 - return tw.err -} - -// Write s into b, terminating it with a NUL if there is room. -// If the value is too long for the field and allowPax is true add a paxheader record instead -func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) { - needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s) - if needsPaxHeader { - paxHeaders[paxKeyword] = s - return - } - if len(s) > len(b) { - if tw.err == nil { - tw.err = ErrFieldTooLong - } - return - } - ascii := toASCII(s) - copy(b, ascii) - if len(ascii) < len(b) { - b[len(ascii)] = 0 - } -} - -// Encode x as an octal ASCII string and write it into b with leading zeros. -func (tw *Writer) octal(b []byte, x int64) { - s := strconv.FormatInt(x, 8) - // leading zeros, but leave room for a NUL. - for len(s)+1 < len(b) { - s = "0" + s - } - tw.cString(b, s, false, paxNone, nil) -} - -// Write x into b, either as octal or as binary (GNUtar/star extension). -// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead -func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) { - // Try octal first. - s := strconv.FormatInt(x, 8) - if len(s) < len(b) { - tw.octal(b, x) - return - } - - // If it is too long for octal, and pax is preferred, use a pax header - if allowPax && tw.preferPax { - tw.octal(b, 0) - s := strconv.FormatInt(x, 10) - paxHeaders[paxKeyword] = s - return - } - - // Too big: use binary (big-endian). - tw.usedBinary = true - for i := len(b) - 1; x > 0 && i >= 0; i-- { - b[i] = byte(x) - x >>= 8 - } - b[0] |= 0x80 // highest bit indicates binary format -} - -var ( - minTime = time.Unix(0, 0) - // There is room for 11 octal digits (33 bits) of mtime. - maxTime = minTime.Add((1<<33 - 1) * time.Second) -) - -// WriteHeader writes hdr and prepares to accept the file's contents. -// WriteHeader calls Flush if it is not the first header. -// Calling after a Close will return ErrWriteAfterClose. -func (tw *Writer) WriteHeader(hdr *Header) error { - return tw.writeHeader(hdr, true) -} - -// WriteHeader writes hdr and prepares to accept the file's contents. -// WriteHeader calls Flush if it is not the first header. -// Calling after a Close will return ErrWriteAfterClose. -// As this method is called internally by writePax header to allow it to -// suppress writing the pax header. -func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { - if tw.closed { - return ErrWriteAfterClose - } - if tw.err == nil { - tw.Flush() - } - if tw.err != nil { - return tw.err - } - - // a map to hold pax header records, if any are needed - paxHeaders := make(map[string]string) - - // TODO(shanemhansen): we might want to use PAX headers for - // subsecond time resolution, but for now let's just capture - // too long fields or non ascii characters - - var header []byte - - // We need to select which scratch buffer to use carefully, - // since this method is called recursively to write PAX headers. - // If allowPax is true, this is the non-recursive call, and we will use hdrBuff. - // If allowPax is false, we are being called by writePAXHeader, and hdrBuff is - // already being used by the non-recursive call, so we must use paxHdrBuff. - header = tw.hdrBuff[:] - if !allowPax { - header = tw.paxHdrBuff[:] - } - copy(header, zeroBlock) - s := slicer(header) - - // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax - pathHeaderBytes := s.next(fileNameSize) - - tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders) - - // Handle out of range ModTime carefully. - var modTime int64 - if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) { - modTime = hdr.ModTime.Unix() - } - - tw.octal(s.next(8), hdr.Mode) // 100:108 - tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116 - tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124 - tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136 - tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - - tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders) - - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297 - tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329 - tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337 - tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345 - - // keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax - prefixHeaderBytes := s.next(155) - tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix - - // Use the GNU magic instead of POSIX magic if we used any GNU extensions. - if tw.usedBinary { - copy(header[257:265], []byte("ustar \x00")) - } - - _, paxPathUsed := paxHeaders[paxPath] - // try to use a ustar header when only the name is too long - if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed { - suffix := hdr.Name - prefix := "" - if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) { - var err error - prefix, suffix, err = tw.splitUSTARLongName(hdr.Name) - if err == nil { - // ok we can use a ustar long name instead of pax, now correct the fields - - // remove the path field from the pax header. this will suppress the pax header - delete(paxHeaders, paxPath) - - // update the path fields - tw.cString(pathHeaderBytes, suffix, false, paxNone, nil) - tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil) - - // Use the ustar magic if we used ustar long names. - if len(prefix) > 0 && !tw.usedBinary { - copy(header[257:265], []byte("ustar\x00")) - } - } - } - } - - // The chksum field is terminated by a NUL and a space. - // This is different from the other octal fields. - chksum, _ := checksum(header) - tw.octal(header[148:155], chksum) - header[155] = ' ' - - if tw.err != nil { - // problem with header; probably integer too big for a field. - return tw.err - } - - if allowPax { - for k, v := range hdr.Xattrs { - paxHeaders[paxXattr+k] = v - } - } - - if len(paxHeaders) > 0 { - if !allowPax { - return errInvalidHeader - } - if err := tw.writePAXHeader(hdr, paxHeaders); err != nil { - return err - } - } - tw.nb = int64(hdr.Size) - tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize - - _, tw.err = tw.w.Write(header) - return tw.err -} - -// writeUSTARLongName splits a USTAR long name hdr.Name. -// name must be < 256 characters. errNameTooLong is returned -// if hdr.Name can't be split. The splitting heuristic -// is compatible with gnu tar. -func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) { - length := len(name) - if length > fileNamePrefixSize+1 { - length = fileNamePrefixSize + 1 - } else if name[length-1] == '/' { - length-- - } - i := strings.LastIndex(name[:length], "/") - // nlen contains the resulting length in the name field. - // plen contains the resulting length in the prefix field. - nlen := len(name) - i - 1 - plen := i - if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize { - err = errNameTooLong - return - } - prefix, suffix = name[:i], name[i+1:] - return -} - -// writePaxHeader writes an extended pax header to the -// archive. -func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error { - // Prepare extended header - ext := new(Header) - ext.Typeflag = TypeXHeader - // Setting ModTime is required for reader parsing to - // succeed, and seems harmless enough. - ext.ModTime = hdr.ModTime - // The spec asks that we namespace our pseudo files - // with the current pid. - pid := os.Getpid() - dir, file := path.Split(hdr.Name) - fullName := path.Join(dir, - fmt.Sprintf("PaxHeaders.%d", pid), file) - - ascii := toASCII(fullName) - if len(ascii) > 100 { - ascii = ascii[:100] - } - ext.Name = ascii - // Construct the body - var buf bytes.Buffer - - for k, v := range paxHeaders { - fmt.Fprint(&buf, paxHeader(k+"="+v)) - } - - ext.Size = int64(len(buf.Bytes())) - if err := tw.writeHeader(ext, false); err != nil { - return err - } - if _, err := tw.Write(buf.Bytes()); err != nil { - return err - } - if err := tw.Flush(); err != nil { - return err - } - return nil -} - -// paxHeader formats a single pax record, prefixing it with the appropriate length -func paxHeader(msg string) string { - const padding = 2 // Extra padding for space and newline - size := len(msg) + padding - size += len(strconv.Itoa(size)) - record := fmt.Sprintf("%d %s\n", size, msg) - if len(record) != size { - // Final adjustment if adding size increased - // the number of digits in size - size = len(record) - record = fmt.Sprintf("%d %s\n", size, msg) - } - return record -} - -// Write writes to the current entry in the tar archive. -// Write returns the error ErrWriteTooLong if more than -// hdr.Size bytes are written after WriteHeader. -func (tw *Writer) Write(b []byte) (n int, err error) { - if tw.closed { - err = ErrWriteTooLong - return - } - overwrite := false - if int64(len(b)) > tw.nb { - b = b[0:tw.nb] - overwrite = true - } - n, err = tw.w.Write(b) - tw.nb -= int64(n) - if err == nil && overwrite { - err = ErrWriteTooLong - return - } - tw.err = err - return -} - -// Close closes the tar archive, flushing any unwritten -// data to the underlying writer. -func (tw *Writer) Close() error { - if tw.err != nil || tw.closed { - return tw.err - } - tw.Flush() - tw.closed = true - if tw.err != nil { - return tw.err - } - - // trailer: two zero blocks - for i := 0; i < 2; i++ { - _, tw.err = tw.w.Write(zeroBlock) - if tw.err != nil { - break - } - } - return tw.err -} diff --git a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer_test.go b/vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer_test.go deleted file mode 100644 index 5e42e322f9c72..0000000000000 --- a/vendor/src/code.google.com/p/go/src/pkg/archive/tar/writer_test.go +++ /dev/null @@ -1,491 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "reflect" - "strings" - "testing" - "testing/iotest" - "time" -) - -type writerTestEntry struct { - header *Header - contents string -} - -type writerTest struct { - file string // filename of expected output - entries []*writerTestEntry -} - -var writerTests = []*writerTest{ - // The writer test file was produced with this command: - // tar (GNU tar) 1.26 - // ln -s small.txt link.txt - // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt - { - file: "testdata/writer.tar", - entries: []*writerTestEntry{ - { - header: &Header{ - Name: "small.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 5, - ModTime: time.Unix(1246508266, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - }, - contents: "Kilts", - }, - { - header: &Header{ - Name: "small2.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 11, - ModTime: time.Unix(1245217492, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - }, - contents: "Google.com\n", - }, - { - header: &Header{ - Name: "link.txt", - Mode: 0777, - Uid: 1000, - Gid: 1000, - Size: 0, - ModTime: time.Unix(1314603082, 0), - Typeflag: '2', - Linkname: "small.txt", - Uname: "strings", - Gname: "strings", - }, - // no contents - }, - }, - }, - // The truncated test file was produced using these commands: - // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt - // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar - { - file: "testdata/writer-big.tar", - entries: []*writerTestEntry{ - { - header: &Header{ - Name: "tmp/16gig.txt", - Mode: 0640, - Uid: 73025, - Gid: 5000, - Size: 16 << 30, - ModTime: time.Unix(1254699560, 0), - Typeflag: '0', - Uname: "dsymonds", - Gname: "eng", - }, - // fake contents - contents: strings.Repeat("\x00", 4<<10), - }, - }, - }, - // The truncated test file was produced using these commands: - // dd if=/dev/zero bs=1048576 count=16384 > (longname/)*15 /16gig.txt - // tar -b 1 -c -f- (longname/)*15 /16gig.txt | dd bs=512 count=8 > writer-big-long.tar - { - file: "testdata/writer-big-long.tar", - entries: []*writerTestEntry{ - { - header: &Header{ - Name: strings.Repeat("longname/", 15) + "16gig.txt", - Mode: 0644, - Uid: 1000, - Gid: 1000, - Size: 16 << 30, - ModTime: time.Unix(1399583047, 0), - Typeflag: '0', - Uname: "guillaume", - Gname: "guillaume", - }, - // fake contents - contents: strings.Repeat("\x00", 4<<10), - }, - }, - }, - // This file was produced using gnu tar 1.17 - // gnutar -b 4 --format=ustar (longname/)*15 + file.txt - { - file: "testdata/ustar.tar", - entries: []*writerTestEntry{ - { - header: &Header{ - Name: strings.Repeat("longname/", 15) + "file.txt", - Mode: 0644, - Uid: 0765, - Gid: 024, - Size: 06, - ModTime: time.Unix(1360135598, 0), - Typeflag: '0', - Uname: "shane", - Gname: "staff", - }, - contents: "hello\n", - }, - }, - }, -} - -// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection. -func bytestr(offset int, b []byte) string { - const rowLen = 32 - s := fmt.Sprintf("%04x ", offset) - for _, ch := range b { - switch { - case '0' <= ch && ch <= '9', 'A' <= ch && ch <= 'Z', 'a' <= ch && ch <= 'z': - s += fmt.Sprintf(" %c", ch) - default: - s += fmt.Sprintf(" %02x", ch) - } - } - return s -} - -// Render a pseudo-diff between two blocks of bytes. -func bytediff(a []byte, b []byte) string { - const rowLen = 32 - s := fmt.Sprintf("(%d bytes vs. %d bytes)\n", len(a), len(b)) - for offset := 0; len(a)+len(b) > 0; offset += rowLen { - na, nb := rowLen, rowLen - if na > len(a) { - na = len(a) - } - if nb > len(b) { - nb = len(b) - } - sa := bytestr(offset, a[0:na]) - sb := bytestr(offset, b[0:nb]) - if sa != sb { - s += fmt.Sprintf("-%v\n+%v\n", sa, sb) - } - a = a[na:] - b = b[nb:] - } - return s -} - -func TestWriter(t *testing.T) { -testLoop: - for i, test := range writerTests { - expected, err := ioutil.ReadFile(test.file) - if err != nil { - t.Errorf("test %d: Unexpected error: %v", i, err) - continue - } - - buf := new(bytes.Buffer) - tw := NewWriter(iotest.TruncateWriter(buf, 4<<10)) // only catch the first 4 KB - big := false - for j, entry := range test.entries { - big = big || entry.header.Size > 1<<10 - if err := tw.WriteHeader(entry.header); err != nil { - t.Errorf("test %d, entry %d: Failed writing header: %v", i, j, err) - continue testLoop - } - if _, err := io.WriteString(tw, entry.contents); err != nil { - t.Errorf("test %d, entry %d: Failed writing contents: %v", i, j, err) - continue testLoop - } - } - // Only interested in Close failures for the small tests. - if err := tw.Close(); err != nil && !big { - t.Errorf("test %d: Failed closing archive: %v", i, err) - continue testLoop - } - - actual := buf.Bytes() - if !bytes.Equal(expected, actual) { - t.Errorf("test %d: Incorrect result: (-=expected, +=actual)\n%v", - i, bytediff(expected, actual)) - } - if testing.Short() { // The second test is expensive. - break - } - } -} - -func TestPax(t *testing.T) { - // Create an archive with a large name - fileinfo, err := os.Stat("testdata/small.txt") - if err != nil { - t.Fatal(err) - } - hdr, err := FileInfoHeader(fileinfo, "") - if err != nil { - t.Fatalf("os.Stat: %v", err) - } - // Force a PAX long name to be written - longName := strings.Repeat("ab", 100) - contents := strings.Repeat(" ", int(hdr.Size)) - hdr.Name = longName - var buf bytes.Buffer - writer := NewWriter(&buf) - if err := writer.WriteHeader(hdr); err != nil { - t.Fatal(err) - } - if _, err = writer.Write([]byte(contents)); err != nil { - t.Fatal(err) - } - if err := writer.Close(); err != nil { - t.Fatal(err) - } - // Simple test to make sure PAX extensions are in effect - if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { - t.Fatal("Expected at least one PAX header to be written.") - } - // Test that we can get a long name back out of the archive. - reader := NewReader(&buf) - hdr, err = reader.Next() - if err != nil { - t.Fatal(err) - } - if hdr.Name != longName { - t.Fatal("Couldn't recover long file name") - } -} - -func TestPaxSymlink(t *testing.T) { - // Create an archive with a large linkname - fileinfo, err := os.Stat("testdata/small.txt") - if err != nil { - t.Fatal(err) - } - hdr, err := FileInfoHeader(fileinfo, "") - hdr.Typeflag = TypeSymlink - if err != nil { - t.Fatalf("os.Stat:1 %v", err) - } - // Force a PAX long linkname to be written - longLinkname := strings.Repeat("1234567890/1234567890", 10) - hdr.Linkname = longLinkname - - hdr.Size = 0 - var buf bytes.Buffer - writer := NewWriter(&buf) - if err := writer.WriteHeader(hdr); err != nil { - t.Fatal(err) - } - if err := writer.Close(); err != nil { - t.Fatal(err) - } - // Simple test to make sure PAX extensions are in effect - if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { - t.Fatal("Expected at least one PAX header to be written.") - } - // Test that we can get a long name back out of the archive. - reader := NewReader(&buf) - hdr, err = reader.Next() - if err != nil { - t.Fatal(err) - } - if hdr.Linkname != longLinkname { - t.Fatal("Couldn't recover long link name") - } -} - -func TestPaxNonAscii(t *testing.T) { - // Create an archive with non ascii. These should trigger a pax header - // because pax headers have a defined utf-8 encoding. - fileinfo, err := os.Stat("testdata/small.txt") - if err != nil { - t.Fatal(err) - } - - hdr, err := FileInfoHeader(fileinfo, "") - if err != nil { - t.Fatalf("os.Stat:1 %v", err) - } - - // some sample data - chineseFilename := "文件名" - chineseGroupname := "組" - chineseUsername := "用戶名" - - hdr.Name = chineseFilename - hdr.Gname = chineseGroupname - hdr.Uname = chineseUsername - - contents := strings.Repeat(" ", int(hdr.Size)) - - var buf bytes.Buffer - writer := NewWriter(&buf) - if err := writer.WriteHeader(hdr); err != nil { - t.Fatal(err) - } - if _, err = writer.Write([]byte(contents)); err != nil { - t.Fatal(err) - } - if err := writer.Close(); err != nil { - t.Fatal(err) - } - // Simple test to make sure PAX extensions are in effect - if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { - t.Fatal("Expected at least one PAX header to be written.") - } - // Test that we can get a long name back out of the archive. - reader := NewReader(&buf) - hdr, err = reader.Next() - if err != nil { - t.Fatal(err) - } - if hdr.Name != chineseFilename { - t.Fatal("Couldn't recover unicode name") - } - if hdr.Gname != chineseGroupname { - t.Fatal("Couldn't recover unicode group") - } - if hdr.Uname != chineseUsername { - t.Fatal("Couldn't recover unicode user") - } -} - -func TestPaxXattrs(t *testing.T) { - xattrs := map[string]string{ - "user.key": "value", - } - - // Create an archive with an xattr - fileinfo, err := os.Stat("testdata/small.txt") - if err != nil { - t.Fatal(err) - } - hdr, err := FileInfoHeader(fileinfo, "") - if err != nil { - t.Fatalf("os.Stat: %v", err) - } - contents := "Kilts" - hdr.Xattrs = xattrs - var buf bytes.Buffer - writer := NewWriter(&buf) - if err := writer.WriteHeader(hdr); err != nil { - t.Fatal(err) - } - if _, err = writer.Write([]byte(contents)); err != nil { - t.Fatal(err) - } - if err := writer.Close(); err != nil { - t.Fatal(err) - } - // Test that we can get the xattrs back out of the archive. - reader := NewReader(&buf) - hdr, err = reader.Next() - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(hdr.Xattrs, xattrs) { - t.Fatalf("xattrs did not survive round trip: got %+v, want %+v", - hdr.Xattrs, xattrs) - } -} - -func TestPAXHeader(t *testing.T) { - medName := strings.Repeat("CD", 50) - longName := strings.Repeat("AB", 100) - paxTests := [][2]string{ - {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"}, - {"a=b", "6 a=b\n"}, // Single digit length - {"a=names", "11 a=names\n"}, // Test case involving carries - {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)}, - {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}} - - for _, test := range paxTests { - key, expected := test[0], test[1] - if result := paxHeader(key); result != expected { - t.Fatalf("paxHeader: got %s, expected %s", result, expected) - } - } -} - -func TestUSTARLongName(t *testing.T) { - // Create an archive with a path that failed to split with USTAR extension in previous versions. - fileinfo, err := os.Stat("testdata/small.txt") - if err != nil { - t.Fatal(err) - } - hdr, err := FileInfoHeader(fileinfo, "") - hdr.Typeflag = TypeDir - if err != nil { - t.Fatalf("os.Stat:1 %v", err) - } - // Force a PAX long name to be written. The name was taken from a practical example - // that fails and replaced ever char through numbers to anonymize the sample. - longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/" - hdr.Name = longName - - hdr.Size = 0 - var buf bytes.Buffer - writer := NewWriter(&buf) - if err := writer.WriteHeader(hdr); err != nil { - t.Fatal(err) - } - if err := writer.Close(); err != nil { - t.Fatal(err) - } - // Test that we can get a long name back out of the archive. - reader := NewReader(&buf) - hdr, err = reader.Next() - if err != nil { - t.Fatal(err) - } - if hdr.Name != longName { - t.Fatal("Couldn't recover long name") - } -} - -func TestValidTypeflagWithPAXHeader(t *testing.T) { - var buffer bytes.Buffer - tw := NewWriter(&buffer) - - fileName := strings.Repeat("ab", 100) - - hdr := &Header{ - Name: fileName, - Size: 4, - Typeflag: 0, - } - if err := tw.WriteHeader(hdr); err != nil { - t.Fatalf("Failed to write header: %s", err) - } - if _, err := tw.Write([]byte("fooo")); err != nil { - t.Fatalf("Failed to write the file's data: %s", err) - } - tw.Close() - - tr := NewReader(&buffer) - - for { - header, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - t.Fatalf("Failed to read header: %s", err) - } - if header.Typeflag != 0 { - t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag) - } - } -} From a4af8ad86963a21865af7aff6a742f43a6341ae4 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Fri, 1 May 2015 18:28:08 -0600 Subject: [PATCH 062/321] Rename development builds to be in the "docker-dev" repo instead of "docker" See https://github.com/docker-library/docker/issues/2 for some of the context around this; essentially, we're looking to create an official `docker` image that includes the Docker CLI (and the dependencies necessary for easy Docker-in-Docker), but it makes sense to use the `docker` namespace for that image. The `docker-dev` official image is already builds of Docker's own `Dockerfile` (but specifically of releases), so this seemed like a good fit. :+1: Signed-off-by: Andrew "Tianon" Page --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 257fefdfeeb95..d13960229a212 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ DOCS_MOUNT := $(if $(DOCSDIR),-v $(CURDIR)/$(DOCSDIR):/$(DOCSDIR)) DOCSPORT := 8000 GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) -DOCKER_IMAGE := docker$(if $(GIT_BRANCH),:$(GIT_BRANCH)) +DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH)) DOCKER_DOCS_IMAGE := docker-docs$(if $(GIT_BRANCH),:$(GIT_BRANCH)) DOCKER_RUN_DOCKER := docker run --rm -it --privileged $(DOCKER_ENVS) $(DOCKER_MOUNT) "$(DOCKER_IMAGE)" From c7cfdb65aa43a4561adb919d170bba5e86d69bee Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Sat, 2 May 2015 03:03:35 +0200 Subject: [PATCH 063/321] Refactor server to use daemon as the service layer in controllers Signed-off-by: Antonio Murdaca --- api/server/server.go | 48 ++++++-------------------------------------- daemon/changes.go | 13 ++++++++++++ daemon/copy.go | 16 +++++++++++++++ daemon/pause.go | 18 +++++++++++++++++ daemon/resize.go | 15 ++++++++++---- daemon/unpause.go | 18 +++++++++++++++++ 6 files changed, 82 insertions(+), 46 deletions(-) create mode 100644 daemon/changes.go create mode 100644 daemon/copy.go create mode 100644 daemon/pause.go create mode 100644 daemon/unpause.go diff --git a/api/server/server.go b/api/server/server.go index 3a7975fe69e24..c43b84fd6b783 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -306,17 +306,10 @@ func (s *Server) postContainersPause(version version.Version, w http.ResponseWri return err } - name := vars["name"] - cont, err := s.daemon.Get(name) - if err != nil { + if err := s.daemon.ContainerPause(vars["name"]); err != nil { return err } - if err := cont.Pause(); err != nil { - return fmt.Errorf("Cannot pause container %s: %s", name, err) - } - cont.LogEvent("pause") - w.WriteHeader(http.StatusNoContent) return nil @@ -330,17 +323,10 @@ func (s *Server) postContainersUnpause(version version.Version, w http.ResponseW return err } - name := vars["name"] - cont, err := s.daemon.Get(name) - if err != nil { + if err := s.daemon.ContainerUnpause(vars["name"]); err != nil { return err } - if err := cont.Unpause(); err != nil { - return fmt.Errorf("Cannot unpause container %s: %s", name, err) - } - cont.LogEvent("unpause") - w.WriteHeader(http.StatusNoContent) return nil @@ -529,13 +515,7 @@ func (s *Server) getContainersChanges(version version.Version, w http.ResponseWr return fmt.Errorf("Missing parameter") } - name := vars["name"] - cont, err := s.daemon.Get(name) - if err != nil { - return err - } - - changes, err := cont.Changes() + changes, err := s.daemon.ContainerChanges(vars["name"]) if err != nil { return err } @@ -1112,12 +1092,7 @@ func (s *Server) postContainersResize(version version.Version, w http.ResponseWr return err } - cont, err := s.daemon.Get(vars["name"]) - if err != nil { - return err - } - - return cont.Resize(height, width) + return s.daemon.ContainerResize(vars["name"], height, width) } func (s *Server) postContainersAttach(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -1371,30 +1346,19 @@ func (s *Server) postContainersCopy(version version.Version, w http.ResponseWrit return fmt.Errorf("Path cannot be empty") } - res := cfg.Resource - - if res[0] == '/' { - res = res[1:] - } - - cont, err := s.daemon.Get(vars["name"]) + data, err := s.daemon.ContainerCopy(vars["name"], cfg.Resource) if err != nil { - logrus.Errorf("%v", err) if strings.Contains(strings.ToLower(err.Error()), "no such id") { w.WriteHeader(http.StatusNotFound) return nil } - } - - data, err := cont.Copy(res) - if err != nil { - logrus.Errorf("%v", err) if os.IsNotExist(err) { return fmt.Errorf("Could not find the file %s in container %s", cfg.Resource, vars["name"]) } return err } defer data.Close() + w.Header().Set("Content-Type", "application/x-tar") if _, err := io.Copy(w, data); err != nil { return err diff --git a/daemon/changes.go b/daemon/changes.go new file mode 100644 index 0000000000000..55b230b9b400e --- /dev/null +++ b/daemon/changes.go @@ -0,0 +1,13 @@ +package daemon + +import "github.com/docker/docker/pkg/archive" + +// ContainerChanges returns a list of container fs changes +func (daemon *Daemon) ContainerChanges(name string) ([]archive.Change, error) { + container, err := daemon.Get(name) + if err != nil { + return nil, err + } + + return container.Changes() +} diff --git a/daemon/copy.go b/daemon/copy.go new file mode 100644 index 0000000000000..dec30d8f3766e --- /dev/null +++ b/daemon/copy.go @@ -0,0 +1,16 @@ +package daemon + +import "io" + +func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) { + container, err := daemon.Get(name) + if err != nil { + return nil, err + } + + if res[0] == '/' { + res = res[1:] + } + + return container.Copy(res) +} diff --git a/daemon/pause.go b/daemon/pause.go new file mode 100644 index 0000000000000..348f83fc72bc0 --- /dev/null +++ b/daemon/pause.go @@ -0,0 +1,18 @@ +package daemon + +import "fmt" + +// ContainerPause pauses a container +func (daemon *Daemon) ContainerPause(name string) error { + container, err := daemon.Get(name) + if err != nil { + return err + } + + if err := container.Pause(); err != nil { + return fmt.Errorf("Cannot pause container %s: %s", name, err) + } + container.LogEvent("pause") + + return nil +} diff --git a/daemon/resize.go b/daemon/resize.go index 060634b13bfc6..f22539466ee80 100644 --- a/daemon/resize.go +++ b/daemon/resize.go @@ -1,12 +1,19 @@ package daemon -func (daemon *Daemon) ContainerExecResize(name string, height, width int) error { - execConfig, err := daemon.getExecConfig(name) +func (daemon *Daemon) ContainerResize(name string, height, width int) error { + container, err := daemon.Get(name) if err != nil { return err } - if err := execConfig.Resize(height, width); err != nil { + + return container.Resize(height, width) +} + +func (daemon *Daemon) ContainerExecResize(name string, height, width int) error { + execConfig, err := daemon.getExecConfig(name) + if err != nil { return err } - return nil + + return execConfig.Resize(height, width) } diff --git a/daemon/unpause.go b/daemon/unpause.go new file mode 100644 index 0000000000000..b13c85e19d315 --- /dev/null +++ b/daemon/unpause.go @@ -0,0 +1,18 @@ +package daemon + +import "fmt" + +// ContainerUnpause unpauses a container +func (daemon *Daemon) ContainerUnpause(name string) error { + container, err := daemon.Get(name) + if err != nil { + return err + } + + if err := container.Unpause(); err != nil { + return fmt.Errorf("Cannot unpause container %s: %s", name, err) + } + container.LogEvent("unpause") + + return nil +} From 4aff563282c4d467575aed006642a22d673d4cb7 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Sat, 2 May 2015 15:57:57 +0200 Subject: [PATCH 064/321] Remove unused error return Signed-off-by: Antonio Murdaca --- daemon/execdriver/native/create.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index fa53621c47ff3..ddb7b9bdccc4d 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -65,9 +65,7 @@ func (d *driver) createContainer(c *execdriver.Command) (*configs.Config, error) return nil, err } - if err := d.setupLabels(container, c); err != nil { - return nil, err - } + d.setupLabels(container, c) d.setupRlimits(container, c) return container, nil } @@ -254,9 +252,7 @@ func (d *driver) setupMounts(container *configs.Config, c *execdriver.Command) e return nil } -func (d *driver) setupLabels(container *configs.Config, c *execdriver.Command) error { +func (d *driver) setupLabels(container *configs.Config, c *execdriver.Command) { container.ProcessLabel = c.ProcessLabel container.MountLabel = c.MountLabel - - return nil } From aaaa8bab0cad495c842580e35322bee8e73de08f Mon Sep 17 00:00:00 2001 From: Raghuram Devarakonda Date: Sat, 2 May 2015 01:30:35 -0400 Subject: [PATCH 065/321] Adding test for "GET /images/(name)/history" API. Closes #12284. Signed-off-by: Raghuram Devarakonda --- integration-cli/docker_api_images_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go index 543182eed1020..c69308ccefb9f 100644 --- a/integration-cli/docker_api_images_test.go +++ b/integration-cli/docker_api_images_test.go @@ -121,3 +121,25 @@ func (s *DockerSuite) TestApiImagesDelete(c *check.C) { c.Assert(status, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) } + +func (s *DockerSuite) TestApiImagesHistory(c *check.C) { + testRequires(c, Network) + name := "test-api-images-history" + out, err := buildImage(name, "FROM hello-world\nENV FOO bar", false) + c.Assert(err, check.IsNil) + + defer deleteImages(name) + id := strings.TrimSpace(out) + + status, body, err := sockRequest("GET", "/images/"+id+"/history", nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusOK) + + var historydata []types.ImageHistory + if err = json.Unmarshal(body, &historydata); err != nil { + c.Fatalf("Error on unmarshal: %s", err) + } + + c.Assert(len(historydata), check.Not(check.Equals), 0) + c.Assert(historydata[0].Tags[0], check.Equals, "test-api-images-history:latest") +} From b447fef7ecb740bc0f9ece75e10926fc5f121b5c Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Sat, 2 May 2015 23:25:57 -0600 Subject: [PATCH 066/321] Update go-patricia to 2.1.0 This includes a fix for the minor v2 API change introduced by https://github.com/tchap/go-patricia/commit/341a37095fea62f400f449e4ec0192702d6e019e. :+1: Signed-off-by: Andrew "Tianon" Page --- hack/vendor.sh | 2 +- pkg/truncindex/truncindex.go | 13 ++-- .../github.com/tchap/go-patricia/README.md | 13 ++-- .../tchap/go-patricia/patricia/children.go | 30 +++++++--- .../tchap/go-patricia/patricia/patricia.go | 59 +++++++++++++++---- .../patricia/patricia_dense_test.go | 2 +- .../patricia/patricia_sparse_test.go | 16 ++--- .../go-patricia/patricia/patricia_test.go | 14 +++++ 8 files changed, 106 insertions(+), 43 deletions(-) diff --git a/hack/vendor.sh b/hack/vendor.sh index 50cff1690f49e..4e8bbefc05a70 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -45,7 +45,7 @@ clone git github.com/gorilla/context 14f550f51a clone git github.com/gorilla/mux e444e69cbd -clone git github.com/tchap/go-patricia v1.0.1 +clone git github.com/tchap/go-patricia v2.1.0 clone hg code.google.com/p/go.net 84a4013f96e0 diff --git a/pkg/truncindex/truncindex.go b/pkg/truncindex/truncindex.go index 73c7e24fb47e2..9aae5c0d08f5e 100644 --- a/pkg/truncindex/truncindex.go +++ b/pkg/truncindex/truncindex.go @@ -14,12 +14,6 @@ var ( ErrAmbiguousPrefix = errors.New("Multiple IDs found with provided prefix") ) -func init() { - // Change patricia max prefix per node length, - // because our len(ID) always 64 - patricia.MaxPrefixPerNode = 64 -} - // TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. // This is used to retrieve image and container IDs by more convenient shorthand prefixes. type TruncIndex struct { @@ -31,8 +25,11 @@ type TruncIndex struct { // NewTruncIndex creates a new TruncIndex and initializes with a list of IDs func NewTruncIndex(ids []string) (idx *TruncIndex) { idx = &TruncIndex{ - ids: make(map[string]struct{}), - trie: patricia.NewTrie(), + ids: make(map[string]struct{}), + + // Change patricia max prefix per node length, + // because our len(ID) always 64 + trie: patricia.NewTrie(patricia.MaxPrefixPerNode(64)), } for _, id := range ids { idx.addID(id) diff --git a/vendor/src/github.com/tchap/go-patricia/README.md b/vendor/src/github.com/tchap/go-patricia/README.md index 11ee4612d326f..9d6ebc43a5889 100644 --- a/vendor/src/github.com/tchap/go-patricia/README.md +++ b/vendor/src/github.com/tchap/go-patricia/README.md @@ -50,9 +50,12 @@ printItem := func(prefix patricia.Prefix, item patricia.Item) error { return nil } -// Create a new tree. +// Create a new default trie (using the default parameter values). trie := NewTrie() +// Create a new custom trie. +trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10)) + // Insert some items. trie.Insert(Prefix("Pepa Novak"), 1) trie.Insert(Prefix("Pepa Sindelar"), 2) @@ -67,12 +70,12 @@ key = Prefix("Karel") fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key)) // Anybody called "Karel" here? true -// Walk the tree. +// Walk the tree in alphabetical order. trie.Visit(printItem) +// "Karel Hynek Macha": 4 +// "Karel Macha": 3 // "Pepa Novak": 1 // "Pepa Sindelar": 2 -// "Karel Macha": 3 -// "Karel Hynek Macha": 4 // Walk a subtree. trie.VisitSubtree(Prefix("Pepa"), printItem) @@ -96,8 +99,8 @@ trie.Delete(Prefix("Karel Macha")) // Walk again. trie.Visit(printItem) -// "Pepa Sindelar": 2 // "Karel Hynek Macha": 10 +// "Pepa Sindelar": 2 // Delete a subtree. trie.DeleteSubtree(Prefix("Pepa")) diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/children.go b/vendor/src/github.com/tchap/go-patricia/patricia/children.go index 07d332633514b..a204b0c8a905c 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/children.go +++ b/vendor/src/github.com/tchap/go-patricia/patricia/children.go @@ -5,11 +5,7 @@ package patricia -// Max prefix length that is kept in a single trie node. -var MaxPrefixPerNode = 10 - -// Max children to keep in a node in the sparse mode. -const MaxChildrenPerSparseNode = 8 +import "sort" type childList interface { length() int @@ -21,13 +17,28 @@ type childList interface { walk(prefix *Prefix, visitor VisitorFunc) error } +type tries []*Trie + +func (t tries) Len() int { + return len(t) +} + +func (t tries) Less(i, j int) bool { + strings := sort.StringSlice{string(t[i].prefix), string(t[j].prefix)} + return strings.Less(0, 1) +} + +func (t tries) Swap(i, j int) { + t[i], t[j] = t[j], t[i] +} + type sparseChildList struct { - children []*Trie + children tries } -func newSparseChildList() childList { +func newSparseChildList(maxChildrenPerSparseNode int) childList { return &sparseChildList{ - children: make([]*Trie, 0, MaxChildrenPerSparseNode), + children: make(tries, 0, DefaultMaxChildrenPerSparseNode), } } @@ -82,6 +93,9 @@ func (list *sparseChildList) next(b byte) *Trie { } func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error { + + sort.Sort(list.children) + for _, child := range list.children { *prefix = append(*prefix, child.prefix...) if child.item != nil { diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go b/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go index 8fcbcdf426c60..a8c3786b62ec6 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go +++ b/vendor/src/github.com/tchap/go-patricia/patricia/patricia.go @@ -13,6 +13,11 @@ import ( // Trie //------------------------------------------------------------------------------ +const ( + DefaultMaxPrefixPerNode = 10 + DefaultMaxChildrenPerSparseNode = 8 +) + type ( Prefix []byte Item interface{} @@ -27,15 +32,44 @@ type Trie struct { prefix Prefix item Item + maxPrefixPerNode int + maxChildrenPerSparseNode int + children childList } // Public API ------------------------------------------------------------------ +type Option func(*Trie) + // Trie constructor. -func NewTrie() *Trie { - return &Trie{ - children: newSparseChildList(), +func NewTrie(options ...Option) *Trie { + trie := &Trie{} + + for _, opt := range options { + opt(trie) + } + + if trie.maxPrefixPerNode <= 0 { + trie.maxPrefixPerNode = DefaultMaxPrefixPerNode + } + if trie.maxChildrenPerSparseNode <= 0 { + trie.maxChildrenPerSparseNode = DefaultMaxChildrenPerSparseNode + } + + trie.children = newSparseChildList(trie.maxChildrenPerSparseNode) + return trie +} + +func MaxPrefixPerNode(value int) Option { + return func(trie *Trie) { + trie.maxPrefixPerNode = value + } +} + +func MaxChildrenPerSparseNode(value int) Option { + return func(trie *Trie) { + trie.maxChildrenPerSparseNode = value } } @@ -85,7 +119,8 @@ func (trie *Trie) MatchSubtree(key Prefix) (matched bool) { return } -// Visit calls visitor on every node containing a non-nil item. +// Visit calls visitor on every node containing a non-nil item +// in alphabetical order. // // If an error is returned from visitor, the function stops visiting the tree // and returns that error, unless it is a special error - SkipSubtree. In that @@ -233,7 +268,7 @@ func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) { // If we are in the root of the trie, reset the trie. if parent == nil { root.prefix = nil - root.children = newSparseChildList() + root.children = newSparseChildList(trie.maxPrefixPerNode) return true } @@ -257,12 +292,12 @@ func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) { ) if node.prefix == nil { - if len(key) <= MaxPrefixPerNode { + if len(key) <= trie.maxPrefixPerNode { node.prefix = key goto InsertItem } - node.prefix = key[:MaxPrefixPerNode] - key = key[MaxPrefixPerNode:] + node.prefix = key[:trie.maxPrefixPerNode] + key = key[trie.maxPrefixPerNode:] goto AppendChild } @@ -306,14 +341,14 @@ AppendChild: // This loop starts with empty node.prefix that needs to be filled. for len(key) != 0 { child := NewTrie() - if len(key) <= MaxPrefixPerNode { + if len(key) <= trie.maxPrefixPerNode { child.prefix = key node.children = node.children.add(child) node = child goto InsertItem } else { - child.prefix = key[:MaxPrefixPerNode] - key = key[MaxPrefixPerNode:] + child.prefix = key[:trie.maxPrefixPerNode] + key = key[trie.maxPrefixPerNode:] node.children = node.children.add(child) node = child } @@ -344,7 +379,7 @@ func (trie *Trie) compact() *Trie { } // Make sure the combined prefixes fit into a single node. - if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode { + if len(trie.prefix)+len(child.prefix) > trie.maxPrefixPerNode { return trie } diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go b/vendor/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go index 346e9a66cbb02..96089fceb44c4 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go +++ b/vendor/src/github.com/tchap/go-patricia/patricia/patricia_dense_test.go @@ -55,7 +55,7 @@ func TestTrie_InsertDensePreceeding(t *testing.T) { trie := NewTrie() start := byte(70) // create a dense node - for i := byte(0); i <= MaxChildrenPerSparseNode; i++ { + for i := byte(0); i <= DefaultMaxChildrenPerSparseNode; i++ { if !trie.Insert(Prefix([]byte{start + i}), true) { t.Errorf("insert failed, prefix=%v", start+i) } diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go b/vendor/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go index 27f3c878b5722..b35c9e2ef5ee7 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go +++ b/vendor/src/github.com/tchap/go-patricia/patricia/patricia_sparse_test.go @@ -300,10 +300,10 @@ func TestTrie_VisitReturnError(t *testing.T) { someErr := errors.New("Something exploded") if err := trie.Visit(func(prefix Prefix, item Item) error { t.Logf("VISITING prefix=%q, item=%v", prefix, item) - if item.(int) == 0 { + if item.(int) == 3 { return someErr } - if item.(int) != 0 { + if item.(int) != 3 { t.Errorf("Unexpected prefix encountered, %q", prefix) } return nil @@ -598,10 +598,10 @@ func ExampleTrie() { // Walk the tree. trie.Visit(printItem) + // "Karel Hynek Macha": 4 + // "Karel Macha": 3 // "Pepa Novak": 1 // "Pepa Sindelar": 2 - // "Karel Macha": 3 - // "Karel Hynek Macha": 4 // Walk a subtree. trie.VisitSubtree(Prefix("Pepa"), printItem) @@ -625,8 +625,8 @@ func ExampleTrie() { // Walk again. trie.Visit(printItem) - // "Pepa Sindelar": 2 // "Karel Hynek Macha": 10 + // "Pepa Sindelar": 2 // Delete a subtree. trie.DeleteSubtree(Prefix("Pepa")) @@ -638,16 +638,16 @@ func ExampleTrie() { // Output: // "Pepa Novak" present? true // Anybody called "Karel" here? true + // "Karel Hynek Macha": 4 + // "Karel Macha": 3 // "Pepa Novak": 1 // "Pepa Sindelar": 2 - // "Karel Macha": 3 - // "Karel Hynek Macha": 4 // "Pepa Novak": 1 // "Pepa Sindelar": 2 // "Karel Hynek Macha": 10 // "Karel Hynek Macha": 10 - // "Pepa Sindelar": 2 // "Karel Hynek Macha": 10 + // "Pepa Sindelar": 2 // "Karel Hynek Macha": 10 } diff --git a/vendor/src/github.com/tchap/go-patricia/patricia/patricia_test.go b/vendor/src/github.com/tchap/go-patricia/patricia/patricia_test.go index ce5ae378fa487..12c441b621ded 100644 --- a/vendor/src/github.com/tchap/go-patricia/patricia/patricia_test.go +++ b/vendor/src/github.com/tchap/go-patricia/patricia/patricia_test.go @@ -13,6 +13,20 @@ import ( // Tests ----------------------------------------------------------------------- +func TestTrie_ConstructorOptions(t *testing.T) { + trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10)) + + if trie.maxPrefixPerNode != 16 { + t.Errorf("Unexpected trie.maxPrefixPerNode value, expected=%v, got=%v", + 16, trie.maxPrefixPerNode) + } + + if trie.maxChildrenPerSparseNode != 10 { + t.Errorf("Unexpected trie.maxChildrenPerSparseNode value, expected=%v, got=%v", + 10, trie.maxChildrenPerSparseNode) + } +} + func TestTrie_GetNonexistentPrefix(t *testing.T) { trie := NewTrie() From e1ccfabdc5c3163db05327d24c4b2581eeac752c Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Sat, 2 May 2015 23:28:15 -0600 Subject: [PATCH 067/321] Update fsnotify to 1.2.0 Signed-off-by: Andrew "Tianon" Page --- hack/vendor.sh | 2 +- .../go-fsnotify/fsnotify/.travis.yml | 8 +- .../github.com/go-fsnotify/fsnotify/AUTHORS | 2 + .../go-fsnotify/fsnotify/CHANGELOG.md | 26 + .../go-fsnotify/fsnotify/CONTRIBUTING.md | 61 ++- .../go-fsnotify/fsnotify/NotUsed.xcworkspace | 0 .../github.com/go-fsnotify/fsnotify/README.md | 34 +- .../go-fsnotify/fsnotify/circle.yml | 26 + .../go-fsnotify/fsnotify/example_test.go | 2 +- .../go-fsnotify/fsnotify/fsnotify.go | 26 +- .../go-fsnotify/fsnotify/inotify.go | 151 ++++-- .../go-fsnotify/fsnotify/inotify_poller.go | 186 +++++++ .../fsnotify/inotify_poller_test.go | 228 +++++++++ .../go-fsnotify/fsnotify/inotify_test.go | 292 +++++++++++ .../go-fsnotify/fsnotify/integration_test.go | 15 + .../github.com/go-fsnotify/fsnotify/kqueue.go | 480 +++++++++--------- 16 files changed, 1200 insertions(+), 339 deletions(-) create mode 100644 vendor/src/github.com/go-fsnotify/fsnotify/NotUsed.xcworkspace create mode 100644 vendor/src/github.com/go-fsnotify/fsnotify/circle.yml create mode 100644 vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go create mode 100644 vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go create mode 100644 vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go diff --git a/hack/vendor.sh b/hack/vendor.sh index 50cff1690f49e..15846ab973827 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -55,7 +55,7 @@ clone git github.com/docker/libtrust 230dfd18c232 clone git github.com/Sirupsen/logrus v0.7.2 -clone git github.com/go-fsnotify/fsnotify v1.0.4 +clone git github.com/go-fsnotify/fsnotify v1.2.0 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml b/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml index f8e76fc66028e..67467e14079ef 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml +++ b/vendor/src/github.com/go-fsnotify/fsnotify/.travis.yml @@ -1,10 +1,12 @@ +sudo: false language: go go: - - 1.2 - - tip + - 1.4.1 + +before_script: + - FIXED=$(go fmt ./... | wc -l); if [ $FIXED -gt 0 ]; then echo "gofmt - $FIXED file(s) not formatted correctly, please run gofmt to fix this." && exit 1; fi -# not yet https://github.com/travis-ci/travis-ci/issues/2318 os: - linux - osx diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS b/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS index 306091eda6a35..4e0e8284e95dc 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS +++ b/vendor/src/github.com/go-fsnotify/fsnotify/AUTHORS @@ -18,8 +18,10 @@ Francisco Souza Hari haran John C Barstow Kelvin Fo +Matt Layher Nathan Youngman Paul Hammond +Pieter Droogendijk Pursuit92 Rob Figueiredo Soge Zhang diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md b/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md index 79f4ddbaa149d..ea9428a2a49dd 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/src/github.com/go-fsnotify/fsnotify/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## v1.2.0 / 2015-02-08 + +* inotify: use epoll to wake up readEvents [#66](https://github.com/go-fsnotify/fsnotify/pull/66) (thanks @PieterD) +* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/go-fsnotify/fsnotify/pull/63) (thanks @PieterD) +* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/go-fsnotify/fsnotify/issues/59) + +## v1.1.1 / 2015-02-05 + +* inotify: Retry read on EINTR [#61](https://github.com/go-fsnotify/fsnotify/issues/61) (thanks @PieterD) + +## v1.1.0 / 2014-12-12 + +* kqueue: rework internals [#43](https://github.com/go-fsnotify/fsnotify/pull/43) + * add low-level functions + * only need to store flags on directories + * less mutexes [#13](https://github.com/go-fsnotify/fsnotify/issues/13) + * done can be an unbuffered channel + * remove calls to os.NewSyscallError +* More efficient string concatenation for Event.String() [#52](https://github.com/go-fsnotify/fsnotify/pull/52) (thanks @mdlayher) +* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/go-fsnotify/fsnotify/issues/48) +* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) + ## v1.0.4 / 2014-09-07 * kqueue: add dragonfly to the build tags. @@ -69,6 +91,10 @@ * no tests for the current implementation * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) +## v0.9.3 / 2014-12-31 + +* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) + ## v0.9.2 / 2014-08-17 * [Backport] Fix missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md b/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md index 2fd0423cca8de..0f377f341b0c1 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md +++ b/vendor/src/github.com/go-fsnotify/fsnotify/CONTRIBUTING.md @@ -1,21 +1,34 @@ # Contributing -* Send questions to [golang-dev@googlegroups.com](mailto:golang-dev@googlegroups.com). - -### Issues +## Issues * Request features and report bugs using the [GitHub Issue Tracker](https://github.com/go-fsnotify/fsnotify/issues). -* Please indicate the platform you are running on. +* Please indicate the platform you are using fsnotify on. +* A code example to reproduce the problem is appreciated. + +## Pull Requests -### Pull Requests +### Contributor License Agreement -A future version of Go will have [fsnotify in the standard library](https://code.google.com/p/go/issues/detail?id=4068), therefore fsnotify carries the same [LICENSE](https://github.com/go-fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so we need you to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). +fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/go-fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/go-fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). Please indicate that you have signed the CLA in your pull request. -To hack on fsnotify: +### How fsnotify is Developed + +* Development is done on feature branches. +* Tests are run on BSD, Linux, OS X and Windows. +* Pull requests are reviewed and [applied to master][am] using [hub][]. + * Maintainers may modify or squash commits rather than asking contributors to. +* To issue a new release, the maintainers will: + * Update the CHANGELOG + * Tag a version, which will become available through gopkg.in. + +### How to Fork + +For smooth sailing, always use the original import path. Installing with `go get` makes this easy. -1. Install as usual (`go get -u github.com/go-fsnotify/fsnotify`) +1. Install from GitHub (`go get -u github.com/go-fsnotify/fsnotify`) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Ensure everything works and the tests pass (see below) 4. Commit your changes (`git commit -am 'Add some feature'`) @@ -27,15 +40,7 @@ Contribute upstream: 3. Push to the branch (`git push fork my-new-feature`) 4. Create a new Pull Request on GitHub -If other team members need your patch before I merge it: - -1. Install as usual (`go get -u github.com/go-fsnotify/fsnotify`) -2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) -3. Pull your revisions (`git fetch fork; git checkout -b my-new-feature fork/my-new-feature`) - -Notice: For smooth sailing, always use the original import path. Installing with `go get` makes this easy. - -Note: The maintainers will update the CHANGELOG on your behalf. Please don't modify it in your pull request. +This workflow is [thoroughly explained by Katrina Owen](https://blog.splice.com/contributing-open-source-git-repositories-go/). ### Testing @@ -43,7 +48,7 @@ fsnotify uses build tags to compile different code on Linux, BSD, OS X, and Wind Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. -To make cross-platform testing easier, I've created a Vagrantfile for Linux and BSD. +To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. * Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) * Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. @@ -51,6 +56,22 @@ To make cross-platform testing easier, I've created a Vagrantfile for Linux and * Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd go-fsnotify/fsnotify; go test'`. * When you're done, you will want to halt or destroy the Vagrant boxes. -Notice: fsnotify file system events don't work on shared folders. The tests get around this limitation by using a tmp directory, but it is something to be aware of. +Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. + +Right now there is no equivalent solution for Windows and OS X, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). + +### Maintainers + +Help maintaining fsnotify is welcome. To be a maintainer: + +* Submit a pull request and sign the CLA as above. +* You must be able to run the test suite on Mac, Windows, Linux and BSD. + +To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. + +All code changes should be internal pull requests. + +Releases are tagged using [Semantic Versioning](http://semver.org/). -Right now I don't have an equivalent solution for Windows and OS X, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). +[hub]: https://github.com/github/hub +[am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/NotUsed.xcworkspace b/vendor/src/github.com/go-fsnotify/fsnotify/NotUsed.xcworkspace new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/README.md b/vendor/src/github.com/go-fsnotify/fsnotify/README.md index 0759284269cd4..7a0b2473645f3 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/README.md +++ b/vendor/src/github.com/go-fsnotify/fsnotify/README.md @@ -2,18 +2,20 @@ [![Coverage](http://gocover.io/_badge/github.com/go-fsnotify/fsnotify)](http://gocover.io/github.com/go-fsnotify/fsnotify) [![GoDoc](https://godoc.org/gopkg.in/fsnotify.v1?status.svg)](https://godoc.org/gopkg.in/fsnotify.v1) +Go 1.3+ required. + Cross platform: Windows, Linux, BSD and OS X. |Adapter |OS |Status | |----------|----------|----------| -|inotify |Linux, Android\*|Supported| -|kqueue |BSD, OS X, iOS\*|Supported| -|ReadDirectoryChangesW|Windows|Supported| +|inotify |Linux, Android\*|Supported [![Build Status](https://travis-ci.org/go-fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/go-fsnotify/fsnotify)| +|kqueue |BSD, OS X, iOS\*|Supported [![Circle CI](https://circleci.com/gh/go-fsnotify/fsnotify.svg?style=svg)](https://circleci.com/gh/go-fsnotify/fsnotify)| +|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| |FSEvents |OS X |[Planned](https://github.com/go-fsnotify/fsnotify/issues/11)| |FEN |Solaris 11 |[Planned](https://github.com/go-fsnotify/fsnotify/issues/12)| |fanotify |Linux 2.6.37+ | | +|USN Journals |Windows |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/53)| |Polling |*All* |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/9)| -| |Plan 9 | | \* Android and iOS are untested. @@ -23,31 +25,35 @@ Please see [the documentation](https://godoc.org/gopkg.in/fsnotify.v1) for usage Two major versions of fsnotify exist. -**[fsnotify.v1](https://gopkg.in/fsnotify.v1)** provides [a new API](https://godoc.org/gopkg.in/fsnotify.v1) based on [this design document](http://goo.gl/MrYxyA). You can import v1 with: +**[fsnotify.v0](https://gopkg.in/fsnotify.v0)** is API-compatible with [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify). Bugfixes *may* be backported, but I recommend upgrading to v1. ```go -import "gopkg.in/fsnotify.v1" +import "gopkg.in/fsnotify.v0" ``` -\* Refer to the package as fsnotify (without the .v1 suffix). +\* Refer to the package as fsnotify (without the .v0 suffix). -**[fsnotify.v0](https://gopkg.in/fsnotify.v0)** is API-compatible with [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify). Bugfixes *may* be backported, but I recommend upgrading to v1. +**[fsnotify.v1](https://gopkg.in/fsnotify.v1)** provides [a new API](https://godoc.org/gopkg.in/fsnotify.v1) based on [this design document](http://goo.gl/MrYxyA). You can import v1 with: ```go -import "gopkg.in/fsnotify.v0" +import "gopkg.in/fsnotify.v1" ``` Further API changes are [planned](https://github.com/go-fsnotify/fsnotify/milestones), but a new major revision will be tagged, so you can depend on the v1 API. -## Contributing +**Master** may have unreleased changes. Use it to test the very latest code or when [contributing][], but don't expect it to remain API-compatible: -* Send questions to [golang-dev@googlegroups.com](mailto:golang-dev@googlegroups.com). -* Request features and report bugs using the [GitHub Issue Tracker](https://github.com/go-fsnotify/fsnotify/issues). +```go +import "github.com/go-fsnotify/fsnotify" +``` -A future version of Go will have [fsnotify in the standard library](https://code.google.com/p/go/issues/detail?id=4068), therefore fsnotify carries the same [LICENSE](https://github.com/go-fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so we need you to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). +## Contributing -Please read [CONTRIBUTING](https://github.com/go-fsnotify/fsnotify/blob/master/CONTRIBUTING.md) before opening a pull request. +Please refer to [CONTRIBUTING][] before opening an issue or pull request. ## Example See [example_test.go](https://github.com/go-fsnotify/fsnotify/blob/master/example_test.go). + + +[contributing]: https://github.com/go-fsnotify/fsnotify/blob/master/CONTRIBUTING.md diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/circle.yml b/vendor/src/github.com/go-fsnotify/fsnotify/circle.yml new file mode 100644 index 0000000000000..204217fb0b20d --- /dev/null +++ b/vendor/src/github.com/go-fsnotify/fsnotify/circle.yml @@ -0,0 +1,26 @@ +## OS X build (CircleCI iOS beta) + +# Pretend like it's an Xcode project, at least to get it running. +machine: + environment: + XCODE_WORKSPACE: NotUsed.xcworkspace + XCODE_SCHEME: NotUsed + # This is where the go project is actually checked out to: + CIRCLE_BUILD_DIR: $HOME/.go_project/src/github.com/go-fsnotify/fsnotify + +dependencies: + pre: + - brew upgrade go + +test: + override: + - go test ./... + +# Idealized future config, eventually with cross-platform build matrix :-) + +# machine: +# go: +# version: 1.4 +# os: +# - osx +# - linux diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go index 9f2c63f475b01..3063796602ce4 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go +++ b/vendor/src/github.com/go-fsnotify/fsnotify/example_test.go @@ -9,7 +9,7 @@ package fsnotify_test import ( "log" - "gopkg.in/fsnotify.v1" + "github.com/go-fsnotify/fsnotify" ) func ExampleNewWatcher() { diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go b/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go index 7b5233f4bbaac..c899ee0083877 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go +++ b/vendor/src/github.com/go-fsnotify/fsnotify/fsnotify.go @@ -7,7 +7,10 @@ // Package fsnotify provides a platform-independent interface for file system notifications. package fsnotify -import "fmt" +import ( + "bytes" + "fmt" +) // Event represents a single file system notification. type Event struct { @@ -30,27 +33,30 @@ const ( // String returns a string representation of the event in the form // "file: REMOVE|WRITE|..." func (e Event) String() string { - events := "" + // Use a buffer for efficient string concatenation + var buffer bytes.Buffer if e.Op&Create == Create { - events += "|CREATE" + buffer.WriteString("|CREATE") } if e.Op&Remove == Remove { - events += "|REMOVE" + buffer.WriteString("|REMOVE") } if e.Op&Write == Write { - events += "|WRITE" + buffer.WriteString("|WRITE") } if e.Op&Rename == Rename { - events += "|RENAME" + buffer.WriteString("|RENAME") } if e.Op&Chmod == Chmod { - events += "|CHMOD" + buffer.WriteString("|CHMOD") } - if len(events) > 0 { - events = events[1:] + // If buffer remains empty, return no event names + if buffer.Len() == 0 { + return fmt.Sprintf("%q: ", e.Name) } - return fmt.Sprintf("%q: %s", e.Name, events) + // Return a list of event names, with leading pipe character stripped + return fmt.Sprintf("%q: %s", e.Name, buffer.String()[1:]) } diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go index f5c0aaef04787..d7759ec8c86b0 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go +++ b/vendor/src/github.com/go-fsnotify/fsnotify/inotify.go @@ -9,6 +9,7 @@ package fsnotify import ( "errors" "fmt" + "io" "os" "path/filepath" "strings" @@ -21,47 +22,66 @@ import ( type Watcher struct { Events chan Event Errors chan error - mu sync.Mutex // Map access - fd int // File descriptor (as returned by the inotify_init() syscall) + mu sync.Mutex // Map access + fd int + poller *fdPoller watches map[string]*watch // Map of inotify watches (key: path) paths map[int]string // Map of watched paths (key: watch descriptor) - done chan bool // Channel for sending a "quit message" to the reader goroutine - isClosed bool // Set to true when Close() is first called + done chan struct{} // Channel for sending a "quit message" to the reader goroutine + doneResp chan struct{} // Channel to respond to Close } // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. func NewWatcher() (*Watcher, error) { + // Create inotify fd fd, errno := syscall.InotifyInit() if fd == -1 { - return nil, os.NewSyscallError("inotify_init", errno) + return nil, errno + } + // Create epoll + poller, err := newFdPoller(fd) + if err != nil { + syscall.Close(fd) + return nil, err } w := &Watcher{ - fd: fd, - watches: make(map[string]*watch), - paths: make(map[int]string), - Events: make(chan Event), - Errors: make(chan error), - done: make(chan bool, 1), + fd: fd, + poller: poller, + watches: make(map[string]*watch), + paths: make(map[int]string), + Events: make(chan Event), + Errors: make(chan error), + done: make(chan struct{}), + doneResp: make(chan struct{}), } go w.readEvents() return w, nil } +func (w *Watcher) isClosed() bool { + select { + case <-w.done: + return true + default: + return false + } +} + // Close removes all watches and closes the events channel. func (w *Watcher) Close() error { - if w.isClosed { + if w.isClosed() { return nil } - w.isClosed = true - // Remove all watches - for name := range w.watches { - w.Remove(name) - } + // Send 'close' signal to goroutine, and set the Watcher to closed. + close(w.done) + + // Wake up goroutine + w.poller.wake() - // Send "quit" message to the reader goroutine - w.done <- true + // Wait for goroutine to close + <-w.doneResp return nil } @@ -69,7 +89,7 @@ func (w *Watcher) Close() error { // Add starts watching the named file or directory (non-recursively). func (w *Watcher) Add(name string) error { name = filepath.Clean(name) - if w.isClosed { + if w.isClosed() { return errors.New("inotify instance already closed") } @@ -88,7 +108,7 @@ func (w *Watcher) Add(name string) error { } wd, errno := syscall.InotifyAddWatch(w.fd, name, flags) if wd == -1 { - return os.NewSyscallError("inotify_add_watch", errno) + return errno } w.mu.Lock() @@ -99,20 +119,33 @@ func (w *Watcher) Add(name string) error { return nil } -// Remove stops watching the the named file or directory (non-recursively). +// Remove stops watching the named file or directory (non-recursively). func (w *Watcher) Remove(name string) error { name = filepath.Clean(name) + + // Fetch the watch. w.mu.Lock() defer w.mu.Unlock() watch, ok := w.watches[name] + + // Remove it from inotify. if !ok { return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) } + // inotify_rm_watch will return EINVAL if the file has been deleted; + // the inotify will already have been removed. + // That means we can safely delete it from our watches, whatever inotify_rm_watch does. + delete(w.watches, name) success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) if success == -1 { - return os.NewSyscallError("inotify_rm_watch", errno) + // TODO: Perhaps it's not helpful to return an error here in every case. + // the only two possible errors are: + // EBADF, which happens when w.fd is not a valid file descriptor of any kind. + // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. + // Watch descriptors are invalidated when they are removed explicitly or implicitly; + // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. + return errno } - delete(w.watches, name) return nil } @@ -128,35 +161,65 @@ func (w *Watcher) readEvents() { buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events n int // Number of bytes read with read() errno error // Syscall errno + ok bool // For poller.wait ) + defer close(w.doneResp) + defer close(w.Errors) + defer close(w.Events) + defer syscall.Close(w.fd) + defer w.poller.close() + for { - // See if there is a message on the "done" channel - select { - case <-w.done: - syscall.Close(w.fd) - close(w.Events) - close(w.Errors) + // See if we have been closed. + if w.isClosed() { return - default: } - n, errno = syscall.Read(w.fd, buf[:]) + ok, errno = w.poller.wait() + if errno != nil { + select { + case w.Errors <- errno: + case <-w.done: + return + } + continue + } - // If EOF is received - if n == 0 { - syscall.Close(w.fd) - close(w.Events) - close(w.Errors) - return + if !ok { + continue } - if n < 0 { - w.Errors <- os.NewSyscallError("read", errno) + n, errno = syscall.Read(w.fd, buf[:]) + // If a signal interrupted execution, see if we've been asked to close, and try again. + // http://man7.org/linux/man-pages/man7/signal.7.html : + // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" + if errno == syscall.EINTR { continue } + + // syscall.Read might have been woken up by Close. If so, we're done. + if w.isClosed() { + return + } + if n < syscall.SizeofInotifyEvent { - w.Errors <- errors.New("inotify: short read in readEvents()") + var err error + if n == 0 { + // If EOF is received. This should really never happen. + err = io.EOF + } else if n < 0 { + // If an error occured while reading. + err = errno + } else { + // Read was too short. + err = errors.New("notify: short read in readEvents()") + } + select { + case w.Errors <- err: + case <-w.done: + return + } continue } @@ -187,7 +250,11 @@ func (w *Watcher) readEvents() { // Send the events that are not ignored on the events channel if !event.ignoreLinux(mask) { - w.Events <- event + select { + case w.Events <- event: + case <-w.done: + return + } } // Move to the next event in the buffer diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go new file mode 100644 index 0000000000000..3b4178404196e --- /dev/null +++ b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller.go @@ -0,0 +1,186 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "errors" + "syscall" +) + +type fdPoller struct { + fd int // File descriptor (as returned by the inotify_init() syscall) + epfd int // Epoll file descriptor + pipe [2]int // Pipe for waking up +} + +func emptyPoller(fd int) *fdPoller { + poller := new(fdPoller) + poller.fd = fd + poller.epfd = -1 + poller.pipe[0] = -1 + poller.pipe[1] = -1 + return poller +} + +// Create a new inotify poller. +// This creates an inotify handler, and an epoll handler. +func newFdPoller(fd int) (*fdPoller, error) { + var errno error + poller := emptyPoller(fd) + defer func() { + if errno != nil { + poller.close() + } + }() + poller.fd = fd + + // Create epoll fd + poller.epfd, errno = syscall.EpollCreate(1) + if poller.epfd == -1 { + return nil, errno + } + // Create pipe; pipe[0] is the read end, pipe[1] the write end. + errno = syscall.Pipe2(poller.pipe[:], syscall.O_NONBLOCK) + if errno != nil { + return nil, errno + } + + // Register inotify fd with epoll + event := syscall.EpollEvent{ + Fd: int32(poller.fd), + Events: syscall.EPOLLIN, + } + errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.fd, &event) + if errno != nil { + return nil, errno + } + + // Register pipe fd with epoll + event = syscall.EpollEvent{ + Fd: int32(poller.pipe[0]), + Events: syscall.EPOLLIN, + } + errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.pipe[0], &event) + if errno != nil { + return nil, errno + } + + return poller, nil +} + +// Wait using epoll. +// Returns true if something is ready to be read, +// false if there is not. +func (poller *fdPoller) wait() (bool, error) { + // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. + // I don't know whether epoll_wait returns the number of events returned, + // or the total number of events ready. + // I decided to catch both by making the buffer one larger than the maximum. + events := make([]syscall.EpollEvent, 7) + for { + n, errno := syscall.EpollWait(poller.epfd, events, -1) + if n == -1 { + if errno == syscall.EINTR { + continue + } + return false, errno + } + if n == 0 { + // If there are no events, try again. + continue + } + if n > 6 { + // This should never happen. More events were returned than should be possible. + return false, errors.New("epoll_wait returned more events than I know what to do with") + } + ready := events[:n] + epollhup := false + epollerr := false + epollin := false + for _, event := range ready { + if event.Fd == int32(poller.fd) { + if event.Events&syscall.EPOLLHUP != 0 { + // This should not happen, but if it does, treat it as a wakeup. + epollhup = true + } + if event.Events&syscall.EPOLLERR != 0 { + // If an error is waiting on the file descriptor, we should pretend + // something is ready to read, and let syscall.Read pick up the error. + epollerr = true + } + if event.Events&syscall.EPOLLIN != 0 { + // There is data to read. + epollin = true + } + } + if event.Fd == int32(poller.pipe[0]) { + if event.Events&syscall.EPOLLHUP != 0 { + // Write pipe descriptor was closed, by us. This means we're closing down the + // watcher, and we should wake up. + } + if event.Events&syscall.EPOLLERR != 0 { + // If an error is waiting on the pipe file descriptor. + // This is an absolute mystery, and should never ever happen. + return false, errors.New("Error on the pipe descriptor.") + } + if event.Events&syscall.EPOLLIN != 0 { + // This is a regular wakeup, so we have to clear the buffer. + err := poller.clearWake() + if err != nil { + return false, err + } + } + } + } + + if epollhup || epollerr || epollin { + return true, nil + } + return false, nil + } +} + +// Close the write end of the poller. +func (poller *fdPoller) wake() error { + buf := make([]byte, 1) + n, errno := syscall.Write(poller.pipe[1], buf) + if n == -1 { + if errno == syscall.EAGAIN { + // Buffer is full, poller will wake. + return nil + } + return errno + } + return nil +} + +func (poller *fdPoller) clearWake() error { + // You have to be woken up a LOT in order to get to 100! + buf := make([]byte, 100) + n, errno := syscall.Read(poller.pipe[0], buf) + if n == -1 { + if errno == syscall.EAGAIN { + // Buffer is empty, someone else cleared our wake. + return nil + } + return errno + } + return nil +} + +// Close all poller file descriptors, but not the one passed to it. +func (poller *fdPoller) close() { + if poller.pipe[1] != -1 { + syscall.Close(poller.pipe[1]) + } + if poller.pipe[0] != -1 { + syscall.Close(poller.pipe[0]) + } + if poller.epfd != -1 { + syscall.Close(poller.epfd) + } +} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go new file mode 100644 index 0000000000000..af9f407f8d928 --- /dev/null +++ b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_poller_test.go @@ -0,0 +1,228 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "syscall" + "testing" + "time" +) + +type testFd [2]int + +func makeTestFd(t *testing.T) testFd { + var tfd testFd + errno := syscall.Pipe(tfd[:]) + if errno != nil { + t.Fatalf("Failed to create pipe: %v", errno) + } + return tfd +} + +func (tfd testFd) fd() int { + return tfd[0] +} + +func (tfd testFd) closeWrite(t *testing.T) { + errno := syscall.Close(tfd[1]) + if errno != nil { + t.Fatalf("Failed to close write end of pipe: %v", errno) + } +} + +func (tfd testFd) put(t *testing.T) { + buf := make([]byte, 10) + _, errno := syscall.Write(tfd[1], buf) + if errno != nil { + t.Fatalf("Failed to write to pipe: %v", errno) + } +} + +func (tfd testFd) get(t *testing.T) { + buf := make([]byte, 10) + _, errno := syscall.Read(tfd[0], buf) + if errno != nil { + t.Fatalf("Failed to read from pipe: %v", errno) + } +} + +func (tfd testFd) close() { + syscall.Close(tfd[1]) + syscall.Close(tfd[0]) +} + +func makePoller(t *testing.T) (testFd, *fdPoller) { + tfd := makeTestFd(t) + poller, err := newFdPoller(tfd.fd()) + if err != nil { + t.Fatalf("Failed to create poller: %v", err) + } + return tfd, poller +} + +func TestPollerWithBadFd(t *testing.T) { + _, err := newFdPoller(-1) + if err != syscall.EBADF { + t.Fatalf("Expected EBADF, got: %v", err) + } +} + +func TestPollerWithData(t *testing.T) { + tfd, poller := makePoller(t) + defer tfd.close() + defer poller.close() + + tfd.put(t) + ok, err := poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + if !ok { + t.Fatalf("expected poller to return true") + } + tfd.get(t) +} + +func TestPollerWithWakeup(t *testing.T) { + tfd, poller := makePoller(t) + defer tfd.close() + defer poller.close() + + err := poller.wake() + if err != nil { + t.Fatalf("wake failed: %v", err) + } + ok, err := poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + if ok { + t.Fatalf("expected poller to return false") + } +} + +func TestPollerWithClose(t *testing.T) { + tfd, poller := makePoller(t) + defer tfd.close() + defer poller.close() + + tfd.closeWrite(t) + ok, err := poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + if !ok { + t.Fatalf("expected poller to return true") + } +} + +func TestPollerWithWakeupAndData(t *testing.T) { + tfd, poller := makePoller(t) + defer tfd.close() + defer poller.close() + + tfd.put(t) + err := poller.wake() + if err != nil { + t.Fatalf("wake failed: %v", err) + } + + // both data and wakeup + ok, err := poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + if !ok { + t.Fatalf("expected poller to return true") + } + + // data is still in the buffer, wakeup is cleared + ok, err = poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + if !ok { + t.Fatalf("expected poller to return true") + } + + tfd.get(t) + // data is gone, only wakeup now + err = poller.wake() + if err != nil { + t.Fatalf("wake failed: %v", err) + } + ok, err = poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + if ok { + t.Fatalf("expected poller to return false") + } +} + +func TestPollerConcurrent(t *testing.T) { + tfd, poller := makePoller(t) + defer tfd.close() + defer poller.close() + + oks := make(chan bool) + live := make(chan bool) + defer close(live) + go func() { + defer close(oks) + for { + ok, err := poller.wait() + if err != nil { + t.Fatalf("poller failed: %v", err) + } + oks <- ok + if !<-live { + return + } + } + }() + + // Try a write + select { + case <-time.After(50 * time.Millisecond): + case <-oks: + t.Fatalf("poller did not wait") + } + tfd.put(t) + if !<-oks { + t.Fatalf("expected true") + } + tfd.get(t) + live <- true + + // Try a wakeup + select { + case <-time.After(50 * time.Millisecond): + case <-oks: + t.Fatalf("poller did not wait") + } + err := poller.wake() + if err != nil { + t.Fatalf("wake failed: %v", err) + } + if <-oks { + t.Fatalf("expected false") + } + live <- true + + // Try a close + select { + case <-time.After(50 * time.Millisecond): + case <-oks: + t.Fatalf("poller did not wait") + } + tfd.closeWrite(t) + if !<-oks { + t.Fatalf("expected true") + } + tfd.get(t) +} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go new file mode 100644 index 0000000000000..035ee8f95d599 --- /dev/null +++ b/vendor/src/github.com/go-fsnotify/fsnotify/inotify_test.go @@ -0,0 +1,292 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux + +package fsnotify + +import ( + "os" + "path/filepath" + "syscall" + "testing" + "time" +) + +func TestInotifyCloseRightAway(t *testing.T) { + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher") + } + + // Close immediately; it won't even reach the first syscall.Read. + w.Close() + + // Wait for the close to complete. + <-time.After(50 * time.Millisecond) + isWatcherReallyClosed(t, w) +} + +func TestInotifyCloseSlightlyLater(t *testing.T) { + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher") + } + + // Wait until readEvents has reached syscall.Read, and Close. + <-time.After(50 * time.Millisecond) + w.Close() + + // Wait for the close to complete. + <-time.After(50 * time.Millisecond) + isWatcherReallyClosed(t, w) +} + +func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) { + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher") + } + w.Add(testDir) + + // Wait until readEvents has reached syscall.Read, and Close. + <-time.After(50 * time.Millisecond) + w.Close() + + // Wait for the close to complete. + <-time.After(50 * time.Millisecond) + isWatcherReallyClosed(t, w) +} + +func TestInotifyCloseAfterRead(t *testing.T) { + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher") + } + + err = w.Add(testDir) + if err != nil { + t.Fatalf("Failed to add .") + } + + // Generate an event. + os.Create(filepath.Join(testDir, "somethingSOMETHINGsomethingSOMETHING")) + + // Wait for readEvents to read the event, then close the watcher. + <-time.After(50 * time.Millisecond) + w.Close() + + // Wait for the close to complete. + <-time.After(50 * time.Millisecond) + isWatcherReallyClosed(t, w) +} + +func isWatcherReallyClosed(t *testing.T, w *Watcher) { + select { + case err, ok := <-w.Errors: + if ok { + t.Fatalf("w.Errors is not closed; readEvents is still alive after closing (error: %v)", err) + } + default: + t.Fatalf("w.Errors would have blocked; readEvents is still alive!") + } + + select { + case _, ok := <-w.Events: + if ok { + t.Fatalf("w.Events is not closed; readEvents is still alive after closing") + } + default: + t.Fatalf("w.Events would have blocked; readEvents is still alive!") + } +} + +func TestInotifyCloseCreate(t *testing.T) { + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher: %v", err) + } + defer w.Close() + + err = w.Add(testDir) + if err != nil { + t.Fatalf("Failed to add testDir: %v", err) + } + h, err := os.Create(filepath.Join(testDir, "testfile")) + if err != nil { + t.Fatalf("Failed to create file in testdir: %v", err) + } + h.Close() + select { + case _ = <-w.Events: + case err := <-w.Errors: + t.Fatalf("Error from watcher: %v", err) + case <-time.After(50 * time.Millisecond): + t.Fatalf("Took too long to wait for event") + } + + // At this point, we've received one event, so the goroutine is ready. + // It's also blocking on syscall.Read. + // Now we try to swap the file descriptor under its nose. + w.Close() + w, err = NewWatcher() + defer w.Close() + if err != nil { + t.Fatalf("Failed to create second watcher: %v", err) + } + + <-time.After(50 * time.Millisecond) + err = w.Add(testDir) + if err != nil { + t.Fatalf("Error adding testDir again: %v", err) + } +} + +func TestInotifyStress(t *testing.T) { + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + testFile := filepath.Join(testDir, "testfile") + + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher: %v", err) + } + defer w.Close() + + killchan := make(chan struct{}) + defer close(killchan) + + err = w.Add(testDir) + if err != nil { + t.Fatalf("Failed to add testDir: %v", err) + } + + proc, err := os.FindProcess(os.Getpid()) + if err != nil { + t.Fatalf("Error finding process: %v", err) + } + + go func() { + for { + select { + case <-time.After(5 * time.Millisecond): + err := proc.Signal(syscall.SIGUSR1) + if err != nil { + t.Fatalf("Signal failed: %v", err) + } + case <-killchan: + return + } + } + }() + + go func() { + for { + select { + case <-time.After(11 * time.Millisecond): + err := w.poller.wake() + if err != nil { + t.Fatalf("Wake failed: %v", err) + } + case <-killchan: + return + } + } + }() + + go func() { + for { + select { + case <-killchan: + return + default: + handle, err := os.Create(testFile) + if err != nil { + t.Fatalf("Create failed: %v", err) + } + handle.Close() + time.Sleep(time.Millisecond) + err = os.Remove(testFile) + if err != nil { + t.Fatalf("Remove failed: %v", err) + } + } + } + }() + + creates := 0 + removes := 0 + after := time.After(5 * time.Second) + for { + select { + case <-after: + if creates-removes > 1 || creates-removes < -1 { + t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes) + } + if creates < 50 { + t.Fatalf("Expected at least 50 creates, got %d", creates) + } + return + case err := <-w.Errors: + t.Fatalf("Got an error from watcher: %v", err) + case evt := <-w.Events: + if evt.Name != testFile { + t.Fatalf("Got an event for an unknown file: %s", evt.Name) + } + if evt.Op == Create { + creates++ + } + if evt.Op == Remove { + removes++ + } + } + } +} + +func TestInotifyRemoveTwice(t *testing.T) { + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + testFile := filepath.Join(testDir, "testfile") + + handle, err := os.Create(testFile) + if err != nil { + t.Fatalf("Create failed: %v", err) + } + handle.Close() + + w, err := NewWatcher() + if err != nil { + t.Fatalf("Failed to create watcher: %v", err) + } + defer w.Close() + + err = w.Add(testFile) + if err != nil { + t.Fatalf("Failed to add testFile: %v", err) + } + + err = os.Remove(testFile) + if err != nil { + t.Fatalf("Failed to remove testFile: %v", err) + } + + err = w.Remove(testFile) + if err != syscall.EINVAL { + t.Fatalf("Expected EINVAL from Remove, got: %v", err) + } + + err = w.Remove(testFile) + if err == syscall.EINVAL { + t.Fatalf("Got EINVAL again, watch was not removed") + } +} diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go b/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go index ad51ab60b2212..59169c6afa881 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go +++ b/vendor/src/github.com/go-fsnotify/fsnotify/integration_test.go @@ -1109,6 +1109,21 @@ func TestConcurrentRemovalOfWatch(t *testing.T) { <-removed2 } +func TestClose(t *testing.T) { + // Regression test for #59 bad file descriptor from Close + testDir := tempMkdir(t) + defer os.RemoveAll(testDir) + + watcher := newWatcher(t) + if err := watcher.Add(testDir); err != nil { + t.Fatalf("Expected no error on Add, got %v", err) + } + err := watcher.Close() + if err != nil { + t.Fatalf("Expected no error on Close, got %v.", err) + } +} + func testRename(file1, file2 string) error { switch runtime.GOOS { case "windows", "plan9": diff --git a/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go b/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go index 5ef1346c0d49b..265622d201df9 100644 --- a/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go +++ b/vendor/src/github.com/go-fsnotify/fsnotify/kqueue.go @@ -14,46 +14,48 @@ import ( "path/filepath" "sync" "syscall" + "time" ) // Watcher watches a set of files, delivering events to a channel. type Watcher struct { - Events chan Event - Errors chan error - mu sync.Mutex // Mutex for the Watcher itself. - kq int // File descriptor (as returned by the kqueue() syscall). - watches map[string]int // Map of watched file descriptors (key: path). - wmut sync.Mutex // Protects access to watches. - enFlags map[string]uint32 // Map of watched files to evfilt note flags used in kqueue. - enmut sync.Mutex // Protects access to enFlags. - paths map[int]string // Map of watched paths (key: watch descriptor). - finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor). - pmut sync.Mutex // Protects access to paths and finfo. - fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events). - femut sync.Mutex // Protects access to fileExists. - externalWatches map[string]bool // Map of watches added by user of the library. - ewmut sync.Mutex // Protects access to externalWatches. - done chan bool // Channel for sending a "quit message" to the reader goroutine - isClosed bool // Set to true when Close() is first called + Events chan Event + Errors chan error + done chan bool // Channel for sending a "quit message" to the reader goroutine + + kq int // File descriptor (as returned by the kqueue() syscall). + + mu sync.Mutex // Protects access to watcher data + watches map[string]int // Map of watched file descriptors (key: path). + externalWatches map[string]bool // Map of watches added by user of the library. + dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue. + paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events. + fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events). + isClosed bool // Set to true when Close() is first called +} + +type pathInfo struct { + name string + isDir bool } // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. func NewWatcher() (*Watcher, error) { - fd, errno := syscall.Kqueue() - if fd == -1 { - return nil, os.NewSyscallError("kqueue", errno) + kq, err := kqueue() + if err != nil { + return nil, err } + w := &Watcher{ - kq: fd, + kq: kq, watches: make(map[string]int), - enFlags: make(map[string]uint32), - paths: make(map[int]string), - finfo: make(map[int]os.FileInfo), + dirFlags: make(map[string]uint32), + paths: make(map[int]pathInfo), fileExists: make(map[string]bool), externalWatches: make(map[string]bool), Events: make(chan Event), Errors: make(chan error), - done: make(chan bool, 1), + done: make(chan bool), } go w.readEvents() @@ -70,73 +72,68 @@ func (w *Watcher) Close() error { w.isClosed = true w.mu.Unlock() - // Send "quit" message to the reader goroutine: - w.done <- true - w.wmut.Lock() + w.mu.Lock() ws := w.watches - w.wmut.Unlock() + w.mu.Unlock() + + var err error for name := range ws { - w.Remove(name) + if e := w.Remove(name); e != nil && err == nil { + err = e + } } + // Send "quit" message to the reader goroutine: + w.done <- true + return nil } // Add starts watching the named file or directory (non-recursively). func (w *Watcher) Add(name string) error { - w.ewmut.Lock() + w.mu.Lock() w.externalWatches[name] = true - w.ewmut.Unlock() + w.mu.Unlock() return w.addWatch(name, noteAllEvents) } // Remove stops watching the the named file or directory (non-recursively). func (w *Watcher) Remove(name string) error { name = filepath.Clean(name) - w.wmut.Lock() + w.mu.Lock() watchfd, ok := w.watches[name] - w.wmut.Unlock() + w.mu.Unlock() if !ok { return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) } - var kbuf [1]syscall.Kevent_t - watchEntry := &kbuf[0] - syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_DELETE) - entryFlags := watchEntry.Flags - success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) - if success == -1 { - return os.NewSyscallError("kevent_rm_watch", errno) - } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { - return errors.New("kevent rm error") + + const registerRemove = syscall.EV_DELETE + if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { + return err } + syscall.Close(watchfd) - w.wmut.Lock() + + w.mu.Lock() + isDir := w.paths[watchfd].isDir delete(w.watches, name) - w.wmut.Unlock() - w.enmut.Lock() - delete(w.enFlags, name) - w.enmut.Unlock() - w.pmut.Lock() delete(w.paths, watchfd) - fInfo := w.finfo[watchfd] - delete(w.finfo, watchfd) - w.pmut.Unlock() + delete(w.dirFlags, name) + w.mu.Unlock() // Find all watched paths that are in this directory that are not external. - if fInfo.IsDir() { + if isDir { var pathsToRemove []string - w.pmut.Lock() - for _, wpath := range w.paths { - wdir, _ := filepath.Split(wpath) - if filepath.Clean(wdir) == filepath.Clean(name) { - w.ewmut.Lock() - if !w.externalWatches[wpath] { - pathsToRemove = append(pathsToRemove, wpath) + w.mu.Lock() + for _, path := range w.paths { + wdir, _ := filepath.Split(path.name) + if filepath.Clean(wdir) == name { + if !w.externalWatches[path.name] { + pathsToRemove = append(pathsToRemove, path.name) } - w.ewmut.Unlock() } } - w.pmut.Unlock() + w.mu.Unlock() for _, name := range pathsToRemove { // Since these are internal, not much sense in propagating error // to the user, as that will just confuse them with an error about @@ -148,37 +145,38 @@ func (w *Watcher) Remove(name string) error { return nil } -const ( - // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) - noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME +// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) +const noteAllEvents = syscall.NOTE_DELETE | syscall.NOTE_WRITE | syscall.NOTE_ATTRIB | syscall.NOTE_RENAME - // Block for 100 ms on each call to kevent - keventWaitTime = 100e6 -) +// keventWaitTime to block on each read from kevent +var keventWaitTime = durationToTimespec(100 * time.Millisecond) -// addWatch adds path to the watched file set. +// addWatch adds name to the watched file set. // The flags are interpreted as described in kevent(2). -func (w *Watcher) addWatch(path string, flags uint32) error { - path = filepath.Clean(path) +func (w *Watcher) addWatch(name string, flags uint32) error { + var isDir bool + // Make ./name and name equivalent + name = filepath.Clean(name) + w.mu.Lock() if w.isClosed { w.mu.Unlock() return errors.New("kevent instance already closed") } + watchfd, alreadyWatching := w.watches[name] + // We already have a watch, but we can still override flags. + if alreadyWatching { + isDir = w.paths[watchfd].isDir + } w.mu.Unlock() - watchDir := false - - w.wmut.Lock() - watchfd, found := w.watches[path] - w.wmut.Unlock() - if !found { - fi, errstat := os.Lstat(path) - if errstat != nil { - return errstat + if !alreadyWatching { + fi, err := os.Lstat(name) + if err != nil { + return err } - // don't watch socket + // Don't watch sockets. if fi.Mode()&os.ModeSocket == os.ModeSocket { return nil } @@ -190,131 +188,96 @@ func (w *Watcher) addWatch(path string, flags uint32) error { // be no file events for broken symlinks. // Hence the returns of nil on errors. if fi.Mode()&os.ModeSymlink == os.ModeSymlink { - path, err := filepath.EvalSymlinks(path) + name, err = filepath.EvalSymlinks(name) if err != nil { return nil } - fi, errstat = os.Lstat(path) - if errstat != nil { + fi, err = os.Lstat(name) + if err != nil { return nil } } - fd, errno := syscall.Open(path, openMode, 0700) - if fd == -1 { - return os.NewSyscallError("Open", errno) + watchfd, err = syscall.Open(name, openMode, 0700) + if watchfd == -1 { + return err } - watchfd = fd - w.wmut.Lock() - w.watches[path] = watchfd - w.wmut.Unlock() - - w.pmut.Lock() - w.paths[watchfd] = path - w.finfo[watchfd] = fi - w.pmut.Unlock() + isDir = fi.IsDir() } - // Watch the directory if it has not been watched before. - w.pmut.Lock() - w.enmut.Lock() - if w.finfo[watchfd].IsDir() && - (flags&syscall.NOTE_WRITE) == syscall.NOTE_WRITE && - (!found || (w.enFlags[path]&syscall.NOTE_WRITE) != syscall.NOTE_WRITE) { - watchDir = true + + const registerAdd = syscall.EV_ADD | syscall.EV_CLEAR | syscall.EV_ENABLE + if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { + syscall.Close(watchfd) + return err } - w.enmut.Unlock() - w.pmut.Unlock() - - w.enmut.Lock() - w.enFlags[path] = flags - w.enmut.Unlock() - - var kbuf [1]syscall.Kevent_t - watchEntry := &kbuf[0] - watchEntry.Fflags = flags - syscall.SetKevent(watchEntry, watchfd, syscall.EVFILT_VNODE, syscall.EV_ADD|syscall.EV_CLEAR) - entryFlags := watchEntry.Flags - success, errno := syscall.Kevent(w.kq, kbuf[:], nil, nil) - if success == -1 { - return errno - } else if (entryFlags & syscall.EV_ERROR) == syscall.EV_ERROR { - return errors.New("kevent add error") + + if !alreadyWatching { + w.mu.Lock() + w.watches[name] = watchfd + w.paths[watchfd] = pathInfo{name: name, isDir: isDir} + w.mu.Unlock() } - if watchDir { - errdir := w.watchDirectoryFiles(path) - if errdir != nil { - return errdir + if isDir { + // Watch the directory if it has not been watched before, + // or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) + w.mu.Lock() + watchDir := (flags&syscall.NOTE_WRITE) == syscall.NOTE_WRITE && + (!alreadyWatching || (w.dirFlags[name]&syscall.NOTE_WRITE) != syscall.NOTE_WRITE) + // Store flags so this watch can be updated later + w.dirFlags[name] = flags + w.mu.Unlock() + + if watchDir { + if err := w.watchDirectoryFiles(name); err != nil { + return err + } } } return nil } -// readEvents reads from the kqueue file descriptor, converts the -// received events into Event objects and sends them via the Events channel +// readEvents reads from kqueue and converts the received kevents into +// Event values that it sends down the Events channel. func (w *Watcher) readEvents() { - var ( - keventbuf [10]syscall.Kevent_t // Event buffer - kevents []syscall.Kevent_t // Received events - twait *syscall.Timespec // Time to block waiting for events - n int // Number of events returned from kevent - errno error // Syscall errno - ) - kevents = keventbuf[0:0] - twait = new(syscall.Timespec) - *twait = syscall.NsecToTimespec(keventWaitTime) + eventBuffer := make([]syscall.Kevent_t, 10) for { // See if there is a message on the "done" channel - var done bool select { - case done = <-w.done: - default: - } - - // If "done" message is received - if done { - errno := syscall.Close(w.kq) - if errno != nil { - w.Errors <- os.NewSyscallError("close", errno) + case <-w.done: + err := syscall.Close(w.kq) + if err != nil { + w.Errors <- err } close(w.Events) close(w.Errors) return + default: } // Get new events - if len(kevents) == 0 { - n, errno = syscall.Kevent(w.kq, nil, keventbuf[:], twait) - - // EINTR is okay, basically the syscall was interrupted before - // timeout expired. - if errno != nil && errno != syscall.EINTR { - w.Errors <- os.NewSyscallError("kevent", errno) - continue - } - - // Received some events - if n > 0 { - kevents = keventbuf[0:n] - } + kevents, err := read(w.kq, eventBuffer, &keventWaitTime) + // EINTR is okay, the syscall was interrupted before timeout expired. + if err != nil && err != syscall.EINTR { + w.Errors <- err + continue } // Flush the events we received to the Events channel for len(kevents) > 0 { - watchEvent := &kevents[0] - mask := uint32(watchEvent.Fflags) - w.pmut.Lock() - name := w.paths[int(watchEvent.Ident)] - fileInfo := w.finfo[int(watchEvent.Ident)] - w.pmut.Unlock() - - event := newEvent(name, mask, false) - - if fileInfo != nil && fileInfo.IsDir() && !(event.Op&Remove == Remove) { - // Double check to make sure the directory exist. This can happen when + kevent := &kevents[0] + watchfd := int(kevent.Ident) + mask := uint32(kevent.Fflags) + w.mu.Lock() + path := w.paths[watchfd] + w.mu.Unlock() + event := newEvent(path.name, mask) + + if path.isDir && !(event.Op&Remove == Remove) { + // Double check to make sure the directory exists. This can happen when // we do a rm -fr on a recursively watched folders and we receive a // modification event first but the folder has been deleted and later // receive the delete event @@ -324,55 +287,49 @@ func (w *Watcher) readEvents() { } } - if fileInfo != nil && fileInfo.IsDir() && event.Op&Write == Write && !(event.Op&Remove == Remove) { + if event.Op&Rename == Rename || event.Op&Remove == Remove { + w.Remove(event.Name) + w.mu.Lock() + delete(w.fileExists, event.Name) + w.mu.Unlock() + } + + if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { w.sendDirectoryChangeEvents(event.Name) } else { // Send the event on the Events channel w.Events <- event } - // Move to next event - kevents = kevents[1:] - - if event.Op&Rename == Rename { - w.Remove(event.Name) - w.femut.Lock() - delete(w.fileExists, event.Name) - w.femut.Unlock() - } if event.Op&Remove == Remove { - w.Remove(event.Name) - w.femut.Lock() - delete(w.fileExists, event.Name) - w.femut.Unlock() - - // Look for a file that may have overwritten this - // (ie mv f1 f2 will delete f2 then create f2) + // Look for a file that may have overwritten this. + // For example, mv f1 f2 will delete f2, then create f2. fileDir, _ := filepath.Split(event.Name) fileDir = filepath.Clean(fileDir) - w.wmut.Lock() + w.mu.Lock() _, found := w.watches[fileDir] - w.wmut.Unlock() + w.mu.Unlock() if found { - // make sure the directory exist before we watch for changes. When we + // make sure the directory exists before we watch for changes. When we // do a recursive watch and perform rm -fr, the parent directory might // have gone missing, ignore the missing directory and let the - // upcoming delete event remove the watch form the parent folder - if _, err := os.Lstat(fileDir); !os.IsNotExist(err) { + // upcoming delete event remove the watch from the parent directory. + if _, err := os.Lstat(fileDir); os.IsExist(err) { w.sendDirectoryChangeEvents(fileDir) + // FIXME: should this be for events on files or just isDir? } } } + + // Move to next event + kevents = kevents[1:] } } } // newEvent returns an platform-independent Event based on kqueue Fflags. -func newEvent(name string, mask uint32, create bool) Event { +func newEvent(name string, mask uint32) Event { e := Event{Name: name} - if create { - e.Op |= Create - } if mask&syscall.NOTE_DELETE == syscall.NOTE_DELETE { e.Op |= Remove } @@ -388,6 +345,11 @@ func newEvent(name string, mask uint32, create bool) Event { return e } +func newCreateEvent(name string) Event { + return Event{Name: name, Op: Create} +} + +// watchDirectoryFiles to mimic inotify when adding a watch on a directory func (w *Watcher) watchDirectoryFiles(dirPath string) error { // Get all files files, err := ioutil.ReadDir(dirPath) @@ -395,36 +357,15 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error { return err } - // Search for new files for _, fileInfo := range files { filePath := filepath.Join(dirPath, fileInfo.Name()) - - if fileInfo.IsDir() == false { - // Watch file to mimic linux fsnotify - e := w.addWatch(filePath, noteAllEvents) - if e != nil { - return e - } - } else { - // If the user is currently watching directory - // we want to preserve the flags used - w.enmut.Lock() - currFlags, found := w.enFlags[filePath] - w.enmut.Unlock() - var newFlags uint32 = syscall.NOTE_DELETE - if found { - newFlags |= currFlags - } - - // Linux gives deletes if not explicitly watching - e := w.addWatch(filePath, newFlags) - if e != nil { - return e - } + if err := w.internalWatch(filePath, fileInfo); err != nil { + return err } - w.femut.Lock() + + w.mu.Lock() w.fileExists[filePath] = true - w.femut.Unlock() + w.mu.Unlock() } return nil @@ -432,7 +373,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error { // sendDirectoryEvents searches the directory for newly created files // and sends them over the event channel. This functionality is to have -// the BSD version of fsnotify match linux fsnotify which provides a +// the BSD version of fsnotify match Linux inotify which provides a // create event for files created in a watched directory. func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { // Get all files @@ -444,36 +385,79 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { // Search for new files for _, fileInfo := range files { filePath := filepath.Join(dirPath, fileInfo.Name()) - w.femut.Lock() + w.mu.Lock() _, doesExist := w.fileExists[filePath] - w.femut.Unlock() + w.mu.Unlock() if !doesExist { - // Send create event (mask=0) - event := newEvent(filePath, 0, true) - w.Events <- event + // Send create event + w.Events <- newCreateEvent(filePath) } - // watchDirectoryFiles (but without doing another ReadDir) - if fileInfo.IsDir() == false { - // Watch file to mimic linux fsnotify - w.addWatch(filePath, noteAllEvents) - } else { - // If the user is currently watching directory - // we want to preserve the flags used - w.enmut.Lock() - currFlags, found := w.enFlags[filePath] - w.enmut.Unlock() - var newFlags uint32 = syscall.NOTE_DELETE - if found { - newFlags |= currFlags - } - - // Linux gives deletes if not explicitly watching - w.addWatch(filePath, newFlags) + // like watchDirectoryFiles (but without doing another ReadDir) + if err := w.internalWatch(filePath, fileInfo); err != nil { + return } - w.femut.Lock() + w.mu.Lock() w.fileExists[filePath] = true - w.femut.Unlock() + w.mu.Unlock() + } +} + +func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) error { + if fileInfo.IsDir() { + // mimic Linux providing delete events for subdirectories + // but preserve the flags used if currently watching subdirectory + w.mu.Lock() + flags := w.dirFlags[name] + w.mu.Unlock() + + flags |= syscall.NOTE_DELETE + return w.addWatch(name, flags) + } + + // watch file to mimic Linux inotify + return w.addWatch(name, noteAllEvents) +} + +// kqueue creates a new kernel event queue and returns a descriptor. +func kqueue() (kq int, err error) { + kq, err = syscall.Kqueue() + if kq == -1 { + return kq, err + } + return kq, nil +} + +// register events with the queue +func register(kq int, fds []int, flags int, fflags uint32) error { + changes := make([]syscall.Kevent_t, len(fds)) + + for i, fd := range fds { + // SetKevent converts int to the platform-specific types: + syscall.SetKevent(&changes[i], fd, syscall.EVFILT_VNODE, flags) + changes[i].Fflags = fflags } + + // register the events + success, err := syscall.Kevent(kq, changes, nil, nil) + if success == -1 { + return err + } + return nil +} + +// read retrieves pending events, or waits until an event occurs. +// A timeout of nil blocks indefinitely, while 0 polls the queue. +func read(kq int, events []syscall.Kevent_t, timeout *syscall.Timespec) ([]syscall.Kevent_t, error) { + n, err := syscall.Kevent(kq, nil, events, timeout) + if err != nil { + return nil, err + } + return events[0:n], nil +} + +// durationToTimespec prepares a timeout value +func durationToTimespec(d time.Duration) syscall.Timespec { + return syscall.NsecToTimespec(d.Nanoseconds()) } From 28547a1a3a594ee24b4cb51a89d9fce630c8c066 Mon Sep 17 00:00:00 2001 From: jmzwcn Date: Fri, 24 Apr 2015 15:02:33 +0800 Subject: [PATCH 068/321] Remove empty line after client.CmdInspect docstring #12706 Signed-off-by: Daniel Zhang Remove empty line after client.CmdInspect docstring fix #12706 Signed-off-by: Daniel Zhang Remove empty line after client.CmdInspect docstring fix #12706 Signed-off-by: Daniel Zhang --- api/client/inspect.go | 1 - 1 file changed, 1 deletion(-) diff --git a/api/client/inspect.go b/api/client/inspect.go index db281795cdb42..e040a0b32db58 100644 --- a/api/client/inspect.go +++ b/api/client/inspect.go @@ -15,7 +15,6 @@ import ( // CmdInspect displays low-level information on one or more containers or images. // // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...] - func (cli *DockerCli) CmdInspect(args ...string) error { cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true) tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template") From 28ea4a63d00e9d0e28375adc80d24461193e7a28 Mon Sep 17 00:00:00 2001 From: Daniel Zhang Date: Thu, 30 Apr 2015 13:38:54 +0800 Subject: [PATCH 069/321] `docker images` friendly duration gets unfriendly after a while fix #12852 Signed-off-by: Daniel Zhang --- pkg/units/duration.go | 3 ++- pkg/units/duration_test.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/units/duration.go b/pkg/units/duration.go index cd331214966b1..a31f780e3be36 100644 --- a/pkg/units/duration.go +++ b/pkg/units/duration.go @@ -26,6 +26,7 @@ func HumanDuration(d time.Duration) string { return fmt.Sprintf("%d weeks", hours/24/7) } else if hours < 24*365*2 { return fmt.Sprintf("%d months", hours/24/30) + } else { + return fmt.Sprintf("%d years", hours/24/365) } - return fmt.Sprintf("%f years", d.Hours()/24/365) } diff --git a/pkg/units/duration_test.go b/pkg/units/duration_test.go index a22947402b82b..fcfb6b7bbd7a0 100644 --- a/pkg/units/duration_test.go +++ b/pkg/units/duration_test.go @@ -41,6 +41,6 @@ func TestHumanDuration(t *testing.T) { assertEquals(t, "13 months", HumanDuration(13*month)) assertEquals(t, "23 months", HumanDuration(23*month)) assertEquals(t, "24 months", HumanDuration(24*month)) - assertEquals(t, "2.010959 years", HumanDuration(24*month+2*week)) - assertEquals(t, "3.164384 years", HumanDuration(3*year+2*month)) + assertEquals(t, "2 years", HumanDuration(24*month+2*week)) + assertEquals(t, "3 years", HumanDuration(3*year+2*month)) } From b40a5eeec8a3db6a6488696f2c58bc4acf0d7dbd Mon Sep 17 00:00:00 2001 From: Travis Thieman Date: Sun, 3 May 2015 14:15:17 -0400 Subject: [PATCH 070/321] Remove stale reference to previous SSH instructions Signed-off-by: Travis Thieman --- docs/sources/examples/running_riak_service.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/sources/examples/running_riak_service.md b/docs/sources/examples/running_riak_service.md index 1b14c3a417b31..7450cd525dba9 100644 --- a/docs/sources/examples/running_riak_service.md +++ b/docs/sources/examples/running_riak_service.md @@ -56,8 +56,7 @@ After that, we modify Riak's configuration: RUN sed -i "s|listener.http.internal = 127.0.0.1:8098|listener.http.internal = 0.0.0.0:8098|" /etc/riak/riak.conf RUN sed -i "s|listener.protobuf.internal = 127.0.0.1:8087|listener.protobuf.internal = 0.0.0.0:8087|" /etc/riak/riak.conf -Then, we expose the Riak Protocol Buffers and HTTP interfaces, along -with SSH: +Then, we expose the Riak Protocol Buffers and HTTP interfaces: # Expose Riak Protocol Buffers and HTTP interfaces EXPOSE 8087 8098 From 5cec69a7b3ea12f9595e017eb27826ff9ad2962b Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 30 Apr 2015 16:23:28 +1000 Subject: [PATCH 071/321] Update the Docker Hub account, org and group documentation and images Signed-off-by: Sven Dowideit --- docs/sources/docker-hub/accounts.md | 45 ++++++++++++++---- docs/sources/docker-hub/hub-images/groups.png | Bin 29958 -> 61631 bytes docs/sources/docker-hub/hub-images/invite.png | Bin 60230 -> 41551 bytes .../hub-images/org-repo-collaborators.png | Bin 0 -> 38785 bytes 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 docs/sources/docker-hub/hub-images/org-repo-collaborators.png diff --git a/docs/sources/docker-hub/accounts.md b/docs/sources/docker-hub/accounts.md index 360eb371f395d..510111f8ef80c 100644 --- a/docs/sources/docker-hub/accounts.md +++ b/docs/sources/docker-hub/accounts.md @@ -34,21 +34,50 @@ page. ## Organizations and groups -Also available on the Docker Hub are organizations and groups that allow -you to collaborate across your organization or team. You can see what -organizations [you belong to and add new organizations]( +A Docker Hub organization contains public and private repositories just like +a user account. Access to push, pull or create these organisation owned repositories +is allocated by defining groups of users and then assigning group rights to +specific repositories. This allows you to distribute limited access +Docker images, and to select which Docker Hub users can publish new images. + +### Creating and viewing organizations + +You can see what organizations [you belong to and add new organizations]( https://hub.docker.com/account/organizations/) from the Account Settings -tab. They are also listed below your user name on your repositories page and in your account profile. +tab. They are also listed below your user name on your repositories page +and in your account profile. ![organizations](/docker-hub/hub-images/orgs.png) -From within your organizations you can create groups that allow you to -further manage who can interact with your repositories. +### Organization groups + +Users in the `Owners` group of an organization can create and modify the +membership of groups. + +Unless they are the organization's `Owner`, users can only see groups of which they +are members. ![groups](/docker-hub/hub-images/groups.png) -You can add or invite users to join groups by clicking on the organization and then clicking the edit button for the group to which you want to add members. Enter a user-name (for current Hub users) or email address (if they are not yet Hub users) for the person you want to invite. They will receive an email invitation to join the group. +### Repository group permissions + +Use organization groups to manage who can interact with your repositories. + +You need to be a member of the organization's `Owners` group to create a new group, +Hub repository or automated build. As an `Owner`, you then delegate the following +repository access rights to groups: + +- `Read` access allows a user to view, search, and pull a private repository in the + same way as they can a public repository. +- `Write` access users are able to push to non-automated repositories on the Docker + Hub. +- `Admin` access allows the user to modify the repositories "Description", "Collaborators" rights, + "Mark as unlisted", "Public/Private" status and "Delete". + +> **Note**: A User who has not yet verified their email address will only have +> `Read` access to the repository, regardless of the rights their group membership +> gives them. -![invite members](/docker-hub/hub-images/invite.png) +![Organization repository collaborators](/docker-hub/hub-images/org-repo-collaborators.png) diff --git a/docs/sources/docker-hub/hub-images/groups.png b/docs/sources/docker-hub/hub-images/groups.png index 0c6430efabb939f19cd45b83b43e04814f171b1e..7b4b1b15314c6e455fb06fb03a40e9fbe43ab70a 100644 GIT binary patch literal 61631 zcmbrm1yCHp*XT=v2bbU)Jh(dq2@*UcxVyW%ORxljTL>Q9-5nMdcXzkNZQmsS`@Q?B z?vtJm7#P?f;Ny&h0F*op6s!Oz z1S2_VNf_wgzh7;I2|x+5gN&v#3=9g+-_I+Uv|>0&`4Evukx!eWXD1498LD=GHHWAS*!J44}m z6M8Y%4t_SW>$eT#a1u26nZxQR!{S(!yX_N|@a~WP`#t66eU}KQ-}Z&+cKeSXU3C;= zi})09b1?^)sX&crVeH1Ij}MQ=;MJRG!Z7OO+nR9c8$IiaIuYx#w$j@dpV>X*a}Oah z$$!W8AH!)DOsv0$3MTHqGZwtqf6o6)LDN+#YiP{5N2ftRrf1*sU57P;OJgNb%umG7 zs%m>qzonvBf3pA7z4v7KK_qsDgon^+!S?0BxQHVz;@_czB{seu=cU*{oIDUbb<(fP z@KP+G{GYan-}R-339Q%&wg1|?FyJ6yBShm{AzLQHKnQ*nHxRsF(F>yyBgKS(p_Ypk zKBbv2rEK^=YVL7(2xZ9?jG9)h47rD+I5c-z1bhxpP`u79#<7|Cf&H&hO&|O&h`K^k z!xvnC!<7t4ilt_vU;jQ>p-%EY76Qd?eGJhW1)gX3hkW$<0_&KSY2eh!n1_gg$^Cnp z$AK{M-BgxMCjVtrOTBEoThQOP(a{qdKe;gABJgmA_^FXFAbJx@_BqZ)e)i&x+^LIs z)c9%=^xLOWgK%z9Ecl!vjAiMKSl7D4hc6_fM&h!lbsNPNQd*>Oh&p8U9@7fVI>=YX z!T;`?BsRW3G7zU{>t*UXDeY1O~)|iu+?ez~eoW2|EQYjpC zX=FVA#*+K{55ccM2xyV??|?%+d~?(2^74|;^O8D0KmX^?p9ZCk_Vo)kaEORFSXg56 z@~AmEIg5r)7eT*R8~9i3GVD6h6q6B?m$v-ZHZBdEf=6KN;wt8p<8BGXg!pu_w9tsE zUL0Q4G>{p2HJGI+yb1aH7$?*B^z;VMm*?%hy)CyEb^=s_7%7lhO$Uxt%S!Bu9UUuc zoUr!|CMl_clM}~}A3qiYFGmo?5+>5&wj@fa5eSjl!yRf%QyMYPkO|0bXckpvzIb4| zHH6cr!`DoF_$snDqt-xH^%sx$?-)R>?AD}2j#(Qi9+G!&$mq^M{V zlc5lnhIF@-o#sG^vd899iS(~1P( z5)vLhRc$IKPn+O&{Pi?w2I-WU-t>HudAceU5OXUIwi0#g)qAj?+VMz_8He)m)p(8_ z9P5+1YC-8z&$nkfBwk{N#*Nja$ScLzoj}%ll}#5zZX$lYQ={23=9CW3@QRxq921`J z>Ze8ypI$31e@E?%Z8aL!B}28tCre0e5_BS%smF?W7xa3w9KCiB+>j_+e9PG~+%lVz zwEVKIe=pb2JJL+DF{Io09669O&Y%+hApbb570&ZaW;BfB+tgnmyuBt(n!T~qaZ7Xh zhIwZwQ!e$UfuCvRy8@+}k+!R0FI60Ssc&@b3XM+EGL3Pgr4fRJxhY#t-PqPhDl6Q> zp}mjTIM6S^a0+B*uuQvQHufuKE8CkBmUdPS zBY})?6j(f=p8~ozW6bZKWg;8q#=j#u>}@$TEjZj!TVJlM>Pd{MO=45+&$5NFR>!LD zzDgJfW^6@=cf(JQ#jQG&L6$P-KRp+C+V%M1-?qbo(GEprq2upUTwQIA6a0MWRGXI} zAcnUoVM8sApiku8Y9dp?`v{Yznlv4~JApEy)}R-q-?yFWH(NL0i~V{4?9Bu+>nf#%H7 zT{v@q`?RDxMxKZ#ZIx^QDQVr*fg{A5XRf!`KW@fY>KdyS~>z{L`kMfbA!)$JD zM#RMQnbq8D?X@N8XpJSlvFKp?;@`@*RL--Xg+5kL`)m_8G- zazHz1(+x8(j}U}P2ExNp3=TJ_!GguKVaGD5^j;eXp4s;m1zB~kI~a1O((`(-5&1y8 zDz)V2(a3CGk#`8HLh^d??gMvXO>yB z-)<>pnWvT+nTKAqI?@!X1u~y)IdCrX)9Y9s)vqN9AYQ7w?6_6vmqOnq$x(_MQ#Xk0 zIdHJXPr1cPuYQx`cxS2~`&K*F*}aV*6U2S`$4x#(DDAC;-ez5eU6Z(Yr&_@^@#t|Z z*x`{Myz~v3m?OnuXEli9Xee&pdCBF))0(6H+nF3>4Qz09#(G=*RoHFMi7Y~g1RfrK zdv{ljNPN_SYtu!bN`r}-ntJ?~7;LE;V?akg8xvFX;Gm4SxHzB3`LB-DB362$7%3-b zXIU8;Wke43DwN5-f~k{&`P1J_?_yue5#$@X@)@G$fOdA3RrpOes&L_@KjUE?k+Q9x zYrUXH-YsD52rzz5105B>y(MI1~hmWOve(q;v13+k2$qW{Q8v*vh6{N z(Pn2XQ)WzZ(;1H){4ZxMLFigKHTQaXU>m>N6P)PjqBnd}^LN+mir3Z{5A_%WORe-V zKe47#uN?3{f7V|g5&z}D2+1N*ER40x%eyfc-_Ad#zw&z-p_`IvvPBn!;TxL_pOTR! z9b6J@vmVst2>Uthozj4h?!%oZCfxEpK2545tH<-Egan%O^GtWRM#B4V*&(UF*=$m` zGvg=E#AI%kvoC~eSwb1p(_C8DK53rbuz1SGo^4}aVn@WCR0UwOZ3oN0dh*x*Xdpk8 zD4FK^EO28uzFonO*~m(+_sOpBLjTTzh@p6D0j|2{el_3Op`bdIWKBklTp zbP723a*?cNPV=sdK)_W#_bZqkw5X|9HTG?{!did5V49st2%+g{kF9l+Am8P<=e#NP zr@Em13`d5oe}<(ti%ZUB9=%2e7l02i*yhHH{m zBtu;b4kF4+%h9IGRN-2xG_{Y}?{3k}*b!+WWhGQmi)!qYT+j=r?NMNiqi2b=>D!@z z>ug4lIXGiy$P%oSHTXA6H z8})3t8Nx70+0$_&`~n>mj1!G1RPnqtD5c98xQFAAp$@p08vEx=LG8r%ya^^MH2Xvg zM5~-my+IlL38`nJJ2fZwulSuvqeM$ncp$>Nu1Z%a6NI4bSk)2O-6!+c9Z0i9qby_?0D`|ID3ah;(# z9WpO-vWD|W*cI9u^o@tvq>0vj1EGN(b*oGG#P8d$BW*2ac)d~Ifal2 zJe_dzwDNqvEQ=x#Bq?i$gN7rw1}9WO<);cbd> z>U;UR?K(#1ve+$8khfah;@sD`leK%u8Dfwx1LJ!&SA(96F|;Z%NsUpTY6zQJ z!VluZ`PMVK5a-(7u^>>-6D>=|4Kh}348~TN1qx#ggpF`L>`u8n4r8xhK7_X^q#O zNz!-Cis9*F9RRnIA+s zP?CTlWYK{9cAJ&?w11iP500v8JH>^UFhdqY0Mr!J$uB>oZ_CP%S z+urf;9Mh(~CY#5$?rJ5$>Ni6wMDbZ-khZF#*2V!7ak+TtA!7;^#(VkyPSVFeFn4N|U{iGf;LC0Yl`{7CL5QyqZ?3waRvF z>Z68V0QL>j?zV1fonAUv0wGXO=IRs8<5#_d9PjL)?lh8Q*0rYUb26fMEl-3Zz{DUz zhQd&jpj9`#Q?oZG7Iv`OpBM#TfV-k^d@$lr2nZfHsS`9Kwa<|jKI3uQM=D(z?y~Z) zX$j?>Wil#tWuBsiriQPs!f!i({9fG*G}(qZ%E5#_g6t<>ibS)IOwi`|oA>~$==}@=f*U#xV3jyHRCX{Fu`Ykz zx$rE}4e+lsXRtH>m8oY`LhD!K_+wY`SC~nAkEi zLA7{QZI3nk`2c!1c`R~GQ#vW4M4#wC?>=-b6Vh&?Tg5Yits zHD%t#xqJ$or|uW2`l;Z*Ggnu;jd3DZ__7nRTG~7^>l-yWFN#U?|&9$ z`|NT`a*u`h#@OOBxFu$%PBZJRziLyXm+S`w@9J3R_?pB9>6jf0za}AC9ld>9+4PKv zle=VdCi~M1-TA=Ad5C{frNrfew-d8yw(~FAfp!y*4s(rUCwzaxM}msN`GaR8sWK6- zS;#X74pw@^+xY}n60Ly@{nQ?7e-iLc3iL9$!R@N8XZYzV=9nOL@LQcSbb~8RzdZ2S zr=5yhXU|Fif4dv7ZHsv3J%z|@c+y3m?gavnQ9T0dB4`sL*VorSO})7Jp`vxQQ;qEDAGM&8s`=FpV_%zS_J3y$F=U9_dH&a-*xhY22$v^#8j zH7#i_lKY?mvU$nR6-;=nVDnDr8iE9?vM;3y3#;!h>SfKL+&ql8aQ8I%b!oSM-UK!6 zoPI0%s+l^az-`X<1;WFkgf>!isn;pz?8%PM8HyOUAYMB&cx5+-WmPQ1@@;9W%c8Ni z|9j;}?!1qCVYU0H(rVzH?6p$bqmJWw{^7+1PEBpgQ}JIJrHLqaM($X&*3|45tLBuj z6|=N!O}ZKpnV7%3OFI6fmo_)PU<0f0`eTfCadwJwu~g+UAjIVLlWlV0jNXVL;a?w7 zRV3w&KDyBlxXY>NSs<29E1|;LEYDXkwBE5yRXbI>*$yz5bvMQm%D-r%x>@53a6!Vx zb|qE~dcKRh*$)|9m%68Bhyd%{(uHFm_x-VMo_dY%i8QCF27>#TSMWqdozeZgn}P7u z<$EiUlR`2^knpyzTDhzfDpsgDtG&X`-CF8xTs z5JA7Hr)WrF7D}usjM_me{He`N14&0DyNK( z`)l3bj7lh?l|Ki_e8ck`RfJlaX*luaX+w=T^!x${8J9TPRtR|vN1*J#o55FZmHSFK zLq)Z*ks)ml?_rr?)@qoqXeLF=+O;9<&LXDHZs_{LoR`tB>{V|lo7>KLY~!(I{dc<< zcS%|(6eJYL@TKAq0ps`rAG+1Qf8hc9Ybm(oH{RrylcW9-LWq_;pU&wwEy=UwjdsYDc4PTbXtBQYbrD)o-25mn!wY1$9T%=70>I|^>fkMcNWyc%e z`r@1%4-I#D4KUZj&+~`t6U06=4sIL*HiGZ&N!M3=w?( zjL2arXZxqcwwCk)RU%E7qI8Ry}*| zr<3k;a2)iTX}iOolET33>;s-%!!DbD*EhKi&3)Dj(&7yPI(%tOP0f4?d;*w};oT@`#`5;d7VPla8px-+0VZGM~yeO>|#rzqR9C;2(D|N0_8LSXGC|3 zykSXHfBgt@4_o*iQCNy9k`gcLllAL>K9gP(E*+=30G+7}1e5re`{KRVnW36__Jdt% z(?Qlp`QJ%=elo9_Y@8@N3d)&{Zs)od3V1QFy1x#!f!gurH~wlScRU2XrPFzO);i*d zXJpETW-WJvpwsa}pb+NVPCTrl{FSV-9 zFFI~-HOltX;xl7bF=;z>bd^2pW}CLLUmu5lC!{b9Vlb=MIUBolcCMB`)3J4Fi|4hv z_9Ly_*+~U%b{T&5CRLvH&6_v$jEsV1RH>a=V!oqNnTm8DKYo1KLxC+s9MjLmX*zxv zEAaB>#R5BhnhUKGhVM@{!uWAqE98qdI@B239qNZEEUjQPF;;Vxt2||{xLqB^E%fpz z=5l!U3_0&GoKsnAbH<(Jbiu)XOC)1^Mkzgv7dH0kWJ z)Q#p-&x-+DD86VZM8-zYxr5Mv2$DKGf_QjKHM8@stQcCIrl+)?w9V5YwAeoonFztt7uB~TdAPQ`)#>Dyz;S8 z-jHj5o@mP5nn8hdIH31Tk(^${fxp0DQ~TPalRNe6?$S_vs(#NX)&ATuLCGEqwWurH zD!M%QMr*9EYKu;z_G)dO6undp1@`eNX+C9Nf0rade_o9?e-(WE33f7SFwtrN5%zv% zZ6mTNiokQ`>9O!=Ip5&=a3*EcEJM*g9J5_)Lze{~Jv^9JA?^FmpCMfaod+&PJn7GM zA?#g3#jcK>H{tAKBPfIZMD}P*Z@3mT zywnfZ<}&$SQL0^~4PDJ1d@cCe;}--{(l_f3Dgb4uWb<14NL7Y`TS@xzCN!fT28{E* z`d%H#%$TEtZRXR-Az-wnyY((X!tr*RgC%#)^F;n9mO7dmsTI$US;qL=gk9PRTN-J) z0TU-z<^oCvcPMvXsowW~a`_(m9BRs^d-@Gi;2o>~k) z5;CU>H?hjjT3{1T7b<=c+CaXRtv)vDYYx6T(a+vu#IGSBfWa;IiuB7?gQOzZ5-R(% zkdG377|Dr2W_<`G%+;qodzrf*;rBB)k7-UYwD$vbs_(Lu%Dt~qvo)~Tn`Y#J)2pPo zYq8!UxE-}?eQpNWn{Nl3hmuaiK=8;;KaBiKML~3PI~FhaG4^XqoWYxVug6IK0Za>S zLIz&VrmEOP%awA$GDPYha@?rpWAif4y>KU(SU=4A{^z5j^cW_)|pLrLxa_LG@&T;ct{{Hik>qtjuriBK@uHvb%*k$z2N~2SKeb6 z`P&&9`IIlml`%l2(mw9a9rB^^gMtL99j}s4UBaB7P+%P!xpyi~@KD6S$qg-UROC1& zBLQ0UlLD+=3vH}uzInAF)G?Yzt}Zh|4K3hH$cNl6K?9$rp-<^DC{c(yN>1B-wiP92 z-oKsY0r2T33;6iV0f6?hzKi1J)hL#C5qxObpp+_bFAmxUwV|+rSo7 z%l$D(fuf?uIVTBUvoR>Er&p7XQBS%lsX{p4nhA0UOB2#(rU+;=#Zv zl+1W06l*dfWStIL{xG+#L1s?~PGPOTxJ!G+6>R+q-ciKwL#Nx|L9xHGP@HOXIPP-; zwZrU{^rJ__BD4}t7@fy*>)WB_xVm6Myz16x*7OQ7|ASj=!_bHyU^5qN3IMGDIJY*O znx4R~9||73GnLlYFYFr9Q&I>HXG-?S^#Px4!q2<`Wn4Jc-k|r2we6#amjngX#Qzlw zpmLV*s+z;&A9_ z8|iZfY?wGW5*{9WKodhk-~hT-n^0l2i_*jv+y#RGJ4Yu~T?w_nv)RKkv*91KPRDRi zFu^3nerG{MD&KQXL1vXK`Q>|PXiMy-qj#sl6*FAigk({dLPdYo@>De$DK=Fx@geDB zFE3N53dVm%?rpi{Q~@p?-seI^QYJhUmgEtA91A3Zg3&@Nm@5F;0)$!>_?Hd~J_7^8 zVv~9k%A~c$*OxKvWxH>gc77mclZ91dAMZPA!OL%aT1EqA9Gh1j^z|(fm1#1usJqPq zhPAP1g{sf=S_5HNcH96gSJFSNI4%@k5OChcFVvF&ye%A)_E$DxIQ!1yjAk24W2I*UdTUL=}(ceLc7@X-V(xv!|so(vk_$M&$-n|L> zcm3ZexU}Yg*3xNvi~XCA+wXtB+5uhxILbM!h~EFZ%m0tN{%=(If3BJ=>}+fPWT{kt zlO*$Yjz@M<@o#v^4@O?3!vgg@j%U7qtxaa+{!btNH^NT-mpGLFje`I0s!gpataWJb zSy*U+z=Z@lrO#SgpJZi`rE+;I7HxQff`cO?A}DBS!vMuY`kz3VAI>gA(@1!@xRtI) zYJPVch`>HdPEU_)XJ-f4e96<|NabPy3R6t%6<~$=K%Z}IR$Ehpf?kt;#De)W`{BeEMuwf6zNuKpDh z1Ok~H&6Z{GIld0=3g|QH0&pcj)thwqG8F+$4XvB7pr)sfh>ypvw_EvSW>yfJht!pa z)}DY*wJ|fd8?UD`%G82)u|CWp;6mP=f|sQ4=?#(bw|-*_ZO))`$E~V)cT38^V080` zMhCi&+g|~0`4+b)GMT&=@6T_0`NOtUU@@syP0g1S5MLI@4y!5U|B8UPS*;>MMSwm6 zt4r=g#J}As&@}c``9>WU7!vew|E%R@o#EkOi-l?$+5}nYujP6<%!Cjb6g;S_VDOs0 z(k#^p@*5<)n$^Y5=0Tn(2c?>TTYpl`?6JJ1Kku-O($Bksp9z+KXSt^zhYxBrgx z*{%~)b$D%sdJc7eKeqHK3prP;UuWiam#tN^?9gVRPpPW_b4=_U)rWF?>jGx|!h;P6 zZ$U*^w&PVS!~vE$K#jPyWL{o6LCt-^Xb%}ep7AG34O7jo)}&vX{=nBYCJ?R^jyvwq zMW4;b6P^UOby+VAJh9V7;AfLbYWs&OkR3ShviX^!ACY{=O&v@@JTsL~7kJqr)nkw8 zy)|4dF(xFD9zC zN528G*(hz<;>Kb;yIVbFSReu60eNhXP3?Jmz2DOQ(C@vq-&h?6JN2w5)T2FXNBlks zrSs9h=iCp)p72j;#XG-?t#5IJoT3R}ISNYZw60)8_NoEyJD!@^omZE<{-Bfw9?q7| zgO&0d;S@)G-g3bss)fF%N-sguW>HB{su|xq@pAnPZNlk*DD^^E%xZ|oWZG1VcWvkZWD^+jniuM+n3ZkTWfS`BEi$DKN=|-yBB&o6r*kB zOl?PQ=823L%->PBCQ3_9l~qysA^HS3i$P|b|M2Wb9UT&8t=eCvBS|{mCv{x*s~Jb- zEwS_S8lBqK`z2y7Eh~UDT|yu;@?WScBloq*6I4*Q{rc|io4%%noEpz2s2U_oOe82nr zBzm-ew=M2h%5fX5u9tuNXZf31iQ??Z{D%R25t<{R8~GrpdxP^*7$+n3bDbrZ{~FM| z*q<5eGq>iGM{Tx0GmSlm>%sYR(`c)h`%C3@BbF~V%)>ToQvLmW&9$+jQ;FFa($G}d z5{w^vFeFVrH;36CmYy>Rm2`t1o~S*mIz9D9XG4~(BE6fuP!O!W&DOM1ko3LNv4l{o zfokGCEt_BJEeXpSM?!cO6a+k+gvLhu8QmRf$-uwYDguL8vg#Yjiy_dtLBY`eat7h$IElO|UZ&=P9)&)!6AQSI z^ltt*DYg!+UcQ$1xxp!D7ftRu8DOJ0)KT{dS_s#wyvfYT%gjMwI4acng>0z+Zmq4i zP3ycou^A52dm*YMNfKw-=k;K9=RoM6n zsJ>Y*_a-w{JNs5pgd6`d%hg=A*i)d>$nt9zf)wd<{^StGjTXMS(#q{ho9j1BO@$x7 zrMR|QyPETebXfF|!O~7w8(*7 z-WItNcUxfl6aLbj^C&U($#G|(b!Nx2pR3H*(@&=*)s@R+wSApuRg3=b4iZ+I+t?t{Mem`CWAt(^_!iiK9Jddy2I9b($Wp{Heog_*x)NLAyspHwGAY?7!oFy4JC_ z1~;uv4D9m$udx&*9d3YnzY~nA(kh!SQI7)xEc`LA;6JUc-JY%acQx?eF7>s(To5_A zjt!c2FG+GmxH2X~2C{=ZE`D^<=7gG*js|}Jvg*|fNMq3EBInMl=KJL$(4zNcvFck6(D zw%B|57KKoB$CXS+8{y|?wfx-4^s~kl;xriX3PXpAu(~y@zNIHhhm%^>t~;HE4SAlj zm3}j9&!dZ%Rp6ekXgy?S1IoiG)mD2=i38Yh+id?siF z4o_fB=2MLG{6f7QPRPv$tABH(Bx68gFAK&+@=Kzs>+T;H^r;}^c4Y+2%54m2 zxJ@VSBH53!852;KG)KSZknr&Fvw60ISy976L$Thz-8o~gwNkzk7PP3+0Cvk1TwJ&< zD|*GA3?9}bQBhIcW9wfH05SoyNSoQl`=fLjXFK`ltko)`1e;~>BfHZMUF%AfexjbZ z+z{)lMXrtJKjd;&;_5u#Lm{oRX-U)j$CP#TC?!0&9}|*rm6JAg{FnmQ>XdSv;LZ4K zmqNPG-zN$JTPOrp)=M7Bvb>0%`wwktBiL`>q94;S;ztNhg^?S*O!EtA#t*hyezlec z+Y*lv6&a_G*ISL+exYja?>>G%aBj0>xzxhwUZyi9Z9R7Zt~+WilrWO$UlR;Lgv}xr zUJjr?G3|Q|Mw=9P!A?<8;H9-b1 zZb?2>iM}80kYa)EyZu1pEvw)3!?p>%Q^w<2?W&~?{^c>EJF`@$DUlrVN0F8GacSns z+gyYufPwGZk$`)Fb?~?rle=Kl>YB6*3gS{dzC0h+!0-wQ1H7t+(A>u? z6Kyde_tk|1(Cr`ffd4o4RI*1*Ms!@pkAmF3-WHO?pE08+{@aTE{YNKASfvvNVGExz zWLzQr76#2S-6boTS#Bq0NFPE5n}m<`bN;*IiS}gOM=sZ`G_1ONS0euuZ#`4~6_s7l z$M^2$bR1`W;qP55okf-$C^PGX99L4HQk-GF&EE+bZy}z@CVR)`)6>OyYqxL^YpdU0 z>w)k~TbsrE*658-w7t9^nnKy5NCo1w~%?l8Qh#*d09ykvlB? zcMA$~9Ch$tLv^w>O+jtqJY^NFF`}pQ;2-noOwgqs`KA`)w#C|fTVdxu&M16w2Ewzp zu@zDHi_OL#sO9cnm(AaoFintyjJ@Z-)Kw%t`R0+M9RJ$Y@e?YNu4o}QyKhlt@HHn9 z>~~#fLGh`zA`Z8{8@ym%t0`HDsTDFljeJM@YQ81r`2E)C-f`LT)ua`2U~c+gsn@s6 zLF%pjViL2ukj~Y!d9Qj^_EHf`3A*#K)+QhBTi0D--T-tw#3MDk%{%_m3a@KfD}>S| z$(izD4qm@PxtNFudZY(9^j8}=1nYl91PKUmVS^3VkNzk~yn=cJ@0o)2ZkjjmE7IdD z4ZD-(BTwfLmd8!)O|?~UdEM+KLI^8qChDAh`BH}Z#VG_6bo1Gl$IIBUJPO@AoZ0-3 zCt*I3&MVN`q`)vbT0VPN?Au#@PTx_Jv6%dZR2mYd!LW9N<6W$Dn@(iVFDdz%PJo9) zkDJcniv`;OIC|T?n7ITkIFOR*4#_uH3YO)8-^}zA)dU zl{Yk&aih=Cm~d?qb#Ia1Mq)N6O;vFzmf2DQ9i#n{A>F6VX!p!)mQgMR4b^&To1`8} z56LDCH=Oon68&FQ ztS#@;`={$t$C`CS6NaF33HPSTg;<4I8thnaeO$Svzr8^-n=KL~yxARa@!qf%@i==F=3DaK61)6y{%*a*HddWk zY>oe;(e1~ru$)A}?Kk6fFio^$TwKwN!KLlvt=abQ z$JXZ!c{6{Hu=~Zl@0A=0qOrbdqH3JNYZshu7Ip}#YF=B%398WHgu>BvQj~L@21Cf@ zEqiI{e&luSk*s||o?XFhn0=GqRYM{zO+LDb&!e;D5)n9{i+TTi?&OM0uxhxLLhpFB z;9<9~-H_^3)Eh@mdy)K3b@*0Z>lVQs`hvEWI_Q6U*jvA2tvEVw17j@UGj!+bOvvM6 zmv0*4O3+ipb)>mhQ%~FzV0NJqzS?5-@d&U{EnPK z#s>{GLP63OReG0sM$r4*P*jK;F4IxGO!&9fzsCQSH?$9VymQWL~~Sd>Z%q4m>d~9gOcwHr3AhuR%^HAKYPI(4uGEOs4(su&3%LS`7yWtXP>D+ zpl9_dWub(Zt=9RFmFP9=w7a+;KcRh0?$*N>KaX3*MJxBgk4cb&?8dDX`T|hqhh(-B z3_wC_b*_lIe*K~VD4J;PeLpf60V;r805;jFB>F6=sYytiFhouuOol<5fE?Z<2E6g( z%?ZSv#kgTf>BxkZdfPmDZJIjts8m|B#fp{gmH8Nv+6(Tu9%J!a=AO%YyB#w{fxt_f zNptJf7oZ^88|P_hwc-!)al z@VVB3l&{~%X?LzDIV4$rmD!{IR)rScS?m?>@uRr2Z00X$Dt9m|ynX!KQMO9U4>;skC0lT3p*C2}ME zVo!x!8T%!7y<~3A-o7mrL>De6@iN_ysaJki2g31?U-2H7IF z*uUGK*SPrjHGt3iFMv}619*v$P!`DXo0wSjV$w%5a19=Le91~qmi|9spnt`0!2IB` zU3_YNwZn?<-M)Q&#o-5O(LNwC?(=-|#)De3){i~!&q+$q&?EhYd=VL^zj@JsgNBQ1 z8rVYuF!X=O9fCR4fp`xTyJl$ zvYy@;z+Imx*%=P3{^TR)TPC-2o%~MF{6}A`2JWm&xi~oD@9sQE&=G+z_PQm16h1PawL%M10W!AX|;c~^AI`~kzf_ldGaeOzYDk?8dPZj zx(djijF!snz+@ii+qD zXUj^c{)2^_7=T>MAxRAljeLha6c29ruCxMu?#!OQeG4S8j)_$ktgEX6-j4oGt)8^q ze_RW|_IyZcXo1|^7-frGU;Z^#vOECDE%DjoRQS*3*w}v`XE_~kTH{B^xayE$gn!b_ zaPc@VYS8$#~C1<+?063C^tgRahRsZHOH$_0tzYQ(` z;X2xDls{0ebpJ%Ah5Zw#2pv;gxc|Vj|2qGD#J}TzqhMQ{4S2_hC+u%SdUYwcczFp` zyxH$4nBJ$>o=SMQ>|gg-fY5!c32(uuLIoYjri2Aa_Ia`oWohR=fzOID_83!;gO! z#A9VO>l|-S6#u0^AG6Hu1*5x3`V)3_u}2JacZHJG=8cs3fMM8=(Bi&|BYM7}iZNaT zr4U$Q|A|P;Vq~q-SzQmxQ-!A5py|B)VD%`4QkdCXv&tv^mqxP~L{{U_rW0@UQG_n=dp)_QewKPmXDAG0Cn#KdjAO%)efJ^l%d( z)yo=5YvODytb42IGg5Qg0s7N#tZ{xoSE_l=(IX*h+`FF&&2IC?D5?I$MqUd1xWIpY z7vK&Gtq)Lkd5s1q5P;Z;tu4TT?Tevq)2q7T9{y zTYpNGXl=mB2>Q4fFh6X`m9>i@TgCH^V>fdhen?|oid6%ycD!$y(ToPuh*qRzvqTuV<|nKUEJOo_(#dC&l79;^ZmI)BUn? z{r)dpZzv+~V11pjzdt@q8r)6r05d)j!-B4SzGrIV>Y22F^IWyuv>`PfO(@T9OSxk| zpru|vl*r&nb#bDhu1Jzw(oi|3ftHcS*75r3_5RdEwP#^?wz;R-d*kOTzv0oc+B|G8 zk&xR^8dLcNhos)Vp%naBy56@=qOzOG#ABSEr$Qjhq3wQmy4xPyccuC+!L-!QKCO)7 z>6w;8v)F+R1 z6VAcyrCL9t`I(a>3txxPndb+Zxyig{>&b)&m9~vLLnlV24iruvl1~2ZhF%Z%*T;^h#G{j# z(rw(fk_mmlvL&Ire2Vy|_zyhG)tlyv$t>Gksz&ijk zof2@^V?rMEJtF*2zs9%4dFW2%Z+t@2&K*44&yU;3#WeJT8~+DwZvhn7)@=)iK#<_> zP4FZ*1h)`8cp$jDyEhUbIDz00q_IEibvIH&-fv$;ew)f|3UJMZxe6Z z_{6r)FiTF}i*5bX^O`-ZGE~A8-6jkeeCw;i>&LK^crVw9{j1E2*QV-xVo|~Ujh^M+ z1GdzLscBS&!fWa5t5KVFAq@Ew`W4G?=c5VdBmt*{Po>0tHlzgNB8s-9%5kdBU5j=3 z(^O-Y9b2qyk(1iF!yg@iJ_WTqbzmYf$g0ZGxi-l)Rqb zI#+=Uc_i2-gpZ<2X7nBz0WJy!zXscH(7z6zSlrJ9q?@^lGiN)#{q_8XNZrro9oL^< zv)GY;6CzrDiBx;M8v?Tcm9vWh{Oo-Vj6}wfa_~FJt*edi-hE8=E(T!``YI3VhdB*YC2Ir;P;saUUPYL+omMP z?+uqb)4~naZUvxnuvdlqgeU!k_P6d$#YeNzCx51uYXZ;lg)P*1(GP83GlQ@n+8VqP zd{5IGoR+uC93ExF?0&8E5~gR>VOxNkfzg?pAT~RX^h=+XSRz73{m=*Hj>lV{eu>3< zk1p`{A`MB#`MB*e&r!z&IYB(L7Pwz@`6yO1Uv zn;e2Ywv}8%dR`_z*B^~N0(UMa+1AF`OS^5IYM2> z*-LJf2&lL<2a_w%>2&JlQvp;x@Z}ifMIiJm@~M;&30wCl{gzsX#u_JLhh2!V!b!u3 zw{``PoDF(L>wTKNu%jihoSY{6!7k;6NrQA!MMy11g@z}7#p}(Z`yW5P=O`!njfdzBIaJ_s>iGUS;fz3jN>#78$X*(Zoc-uXD5&xO63X z05A3QO=vLovaH9UkfjO`U+l9AsS6z#V(Kbur|TbuVHE1>>#LZ*_g*wy1xHxRBZo8Ak6(i^E)=qFW8j|AqK zf@Kb4IvYtWR?Kw-C27y}GOzcG;$NqJ41A{P|oW&<;?{Y#9I z^M>tXZt1bleMpu{0&x#4-*6a+@|KkdxI8q=hHG;q01X9o@(OsBY zoI#ByePv&DHQ?flC)3yzzyaDGbepJays^FlI+~_H7d2z7Xi0J(?0~<9XdQNy40UB* z&0HB2PfAKdU&nt<`;0d~&t@vcBh&ptpSU+#^3w#pKJMDb8s-k_yyier}9<#FNEEDI*IlwZUv?<%?Z#{skr zBHk$i!*JA6KgcFJSYn@-S5IPb-HnEGuk$(6nJ*x(ReOJ!@*73C0BhHAU8WF5IozL% zZFpIqdy)`~_FK`T&~KAEYn% zWQ`p%E}MAzTZ=3@ikMeWac?|sVs1X-VJ{uJyy&wzT=RAt43?M=HQozul_a{ma_r)& zaDy$R)R5TiJ(dBltX9>b8%*D5wst+6#_y4f?yg<_UHh*b!%>M6Tbc#NLsB?WqRm?N zburg;!|99dwiy`9a4E-gqs!>Nb?jCUTbDB_H&4TiSTg6kG1E{`cR)uI!SDv@#jMK# zx!jc1ev*J4U>F1kds+=nFt?R3g5;tWa6MV;*U3E9c~vF1OJs7d)gW-NYO9H1E9s7T z)=EgrE*y93n;qB|L%VER+my*RJE#?FXs?AF=$tO)WYBWdiNj|bq4}?8TseOxgsk==cT`-i3aHfx-4Z)p;%LpbLX8pAVO9^3g3 zt_(+ksKi)bIIej3DxE)4;c|-W#l(4}yCL3i=}d9{rU;y+`HT>QH`hKlKAZeqnmT00 z!a9qLn@TsFW-V9;EhJ5@VW$RJ=A+4EWcDRUXJXG076_=kZc1g;xoZv3o^{)K{qz0c zk#)=g=#@on-p}J!{8=m(w)fwISPT*QVQT=KiV0r3(z!{b6B_@)t$Ea1B6Lf6*zz zrr?TEhQL~%2J6%{eI$W)^WHaMxU5sp;(5d zwCQkLb*Abp?r3=>1yn1DFhdd;$sCSGZiG0^hL$ESts)xo7<5Pz##7-fY9;dw8;v_f z5iWwlUXI7d137J04?awLFHfbgH&o`F#T=690I>-8{4Vg7H5`u(#;()G@c+HMp!w@sKjw(x)oS}< z+;jab=kAaGxjE7E>LL4rcmdOJYmYayb@(m{EANz)l0qqA24PiD^Rumz_`}JwBM#*prv^VYCwPV=)vd2SxFCoZ zu6~fleUTqNZq`Oc7>7z zd-(eXOc(JY^(P(I=7?8ndBg0iA5CCw=~YCEiTYl)l5urxvwKO{XL|ZGJ5__ zvtDM9TJ7*X$8D+_MBPhL74wzb6jmVW zva`)l)^S;_Hvfbl!9c*i!+b%wOqN*u-wA(ou%Y1K~xN5{vxTM}x&Z?)JHNRY!K^K?`Mj#y~eJmi>v+B_=tP``Pbo&&E&@tk9e zrsDU8W+KC%-fC-xKGRP)+L9d~YD;K) z3eDo)+gCD|%r-?P`9fmAH>0Tiwju1)vD;oBcfZ(=CEl$c>SI>X8kanN{qp9nN1aIS zyRw!y-o3Px3>a}U)eft;cMO4DH`Ut-5k$xsg_KmGZU&)#ttQJrS*R4n;jm`hxRu=rdW+tW2zjf#oO~@ z@NY*tJ2BD`6hdg*+qUzz8EQQ#uJCi+{VSNZ=deZg2YPN#R!lz_n$qjN+*x1 zV^N@Ogq7`8nKcF_G54C9^me*bdKY@&CG%7|%wO=!>4qje%R}6jdV9E~GZ)Bh>Lsu% z%FpeT`f+UgKDtM=rO`cNJujNAve0KlR*u@%1x_!CBpviL0JIBUy><%% z$wIfbLB3r}OON${JR7?3Q({;=>%RW*ZNrd?+nZV=Dw3wG#KxURTH=ffja=e;N3)wa()Fwdc>{pMu{eJ&f8OWlZ~;CC)+{J>EZ{)KNE zt9}q}P+eu#*BTqrt5E8EOHfXIk8H7OKlmNa<&L|&92Q?YQ|0ED3jnb`!2QgFo0jP> zm`#(5dB7zK20mXoAph9*y+i}pQ{SwNC8Hc*jY{MGond1_Sa#r-q9?D8Hbt^i+$YbK zr3vYHV|EWtA_58Qm1jJa2Rz>_4fwe=x;f$x%4^z3ZwXPa^_paNOPmdQX?| z!d}{8V+FHi`Vj9oL3#E3Rp!fUsc5RUJzefO{%7_uAoUNy^%ypP0SqC(CjrY5o_7Y6}V*Oi`g*&NLL069&rmXc5Hy(&r7Bz%2wUh1Y#ab$&aNgLhWr99Z1Hs!bN$T~|HRo5FBz8!OYvStcZ#@2!R?NODKTP{(oBoYBs8*nhJkNsnCKvM(ILr8$Y{*kc7-_XkBchh&1$ zjA}6*!IO9M(_f$CUyr7W3snY8iz=DQ{|>AGi& z5jaO1HV^)|GM|hq3Sq8{q4y)Cxp9?Dc4`4d7I6DoWGT(zU0Tx`*PFTen54Bw#T;?4Z{pDm1%|n24m)F4uP;-IKR6 zRj_}V%20&|UbLN<>xc<$K2m*unn)&1Z_)YegE`mNAfujMqr<>kc^aXrjAVArtRHJ~ z$82oudxlMpk&^}O;TrE-9VNaFZE)->hB^f|cPOewjo1gtB@fS);5Ukk*_xaxiLxm` zIrXlrY#CU;Q>~L~*jW}pt|Rl2N8RALtO}WW_XhfBMWxwWGg^XTYC}$o@tvYDtBZ>`@2f%m{bRQ1H-+zJx~lZPTeHOpuToi!@T(}#XH)B7SbhJ^t6x9jmPu!_7++I4hVMxv%{RZ{^ClE&s?X=e zL$Fh|{o1KOdo+-TAl5l`gRf9>EfYdC-*6f1fNr?k zcG{8JOJ|*R>37EB{lPOB%P~>=h}d5+B!wU6x+vV75MNcUyP+mG8X@n zy!L_hoH#vB^sdyH)dR|XW<-{%PaemX*C6w_1CP z{M^H*qp!<@)MD`X=f_-~&X7G1#SH~q9Hgnix)L3zu$H6mMvm|z07c5#-L7AYf59c! z0ybMH5{Xhg%3~kniAAfT{{;HRGZrR zJP&CGsh$j=Dtqj~$r!or3B}a0KuWDTK{MkWQ1MRRVf{oIA|u2}IrepbJ1JH_vRB7I z?>N}B5Z4u5MB&p5T~*|aOh0POqpQoFtvXzNke0GC{Q#dA9OAwyN2P1Q*FpS9^tIJA z+9ShemdVS!UKz8Et{a;R;^-m=wk8i_hrXZYa2cFS|I)Kd7GaIGH}->h+hM~c&-@uGkLGYD*=YyZ*Sx;*) zvCk#+_hkO?x`3gkofXVEX_;C0++AgBA?O0h_ok_WEHoQdj3FcPAry z<+!oOb1Xo2%4mD*Xg#_}SofFH(eh*0ir_Qvo9T-?11RDBS&oU$sN43=rM@H?BsuFC zY;-nvFEji==a9%8R$%`M{dxzp=XC`}I3tH)kAT9$mm^Ek=q=|9Gv|xAASgwocWdVSK;tn+F)l~7@2XwGLeNJtsK>s{dbXG3q(k}#Yrd)0J; zng@ew+D`eRMLsNqqXF;-3xZhQ*luE}Amv%{AKEf4xx3iZP5_XGPK=b%?F~6jv(u^B z9e7Ev)z(Lkd1JPOB{PJYV41XBBySW4e#xU&oL8WW7!bR~}_iTm4;K?-%ua<7J$k$+GDFMx1%_T*#%cgv)K4N;yYj868*BTr&%uXRn$X?`;31 zLq^D&OnSUg7J*u$Hc3<@P)MV5xuLVvbkH!ytg)wu670^SBR-r1gLCrrE?v6roL@*! zHGGZ1{QSwOgd>&$A>Em}<%q0H2U7TuA5`Vb)TPcnD(1A$?C=#Xh-Ig`T}*KqDRkG+ zu5NPYD7PA#ydVlSK2#_@F0lLv*A3i@^hh{hIWO7=Q|@0ZB3xWhZf~(6ymfmwcER1Xycybb3IzH#7oxg4i*uN+^9It`*?L};|^XnDy$365W=kd zm4*qp4caKL=M!a#y0e=b{+h-dUXW=JO_lf=oUE3uw#q5|tu~m2xmdsh?SOPYzkKfo z0m^HL&z8SWjC-M--Hu%#uY1Hx%j^zu`Jz*Yw{E8_0q4=sB0T0wluyfenO~sL$rfh8 zigwnccEI9G5LJJok`jDMikz}Kep7}dID8PMEwQPh_(JWS?8R@Fb2BWhnH0Uj3LO?# zJ=G&0%miERPH9$KuU00~4|Wx%wQ%oi z=~d=eLqf+;1ud49RSxZvGe+{0UB2BP0a^C2*8~ppdF`?;E}B_5MAn+~EeaX23 z5E5%X5}zk*8+g?AOGls*ly*G6G4%C@29{n#cvEUsM5Rh^2b&?%ZR$QG*u1wU6G&%!;Ek5}7wf;BtC>Gi9YUB+Fj=8VO* zaA7vdq1B@;5$8(}%hK+LHo^5H!eC#nn&M2bVCZRM_(BOZ_$sJ*)JD%`->k3MexSnm z-7mX_sOxrxZXWkrK%@7<33!{a4zg?{U?N!}bUhkwS$_88?eG;DnSG_z(M~PES7ZGM)zVKEeu-K^2U!_Ze zj=iAm{Rb7E&ok$WAo`?IeYyvnWmBU^HlmG!sOpd&CMs|V>QSCT`9;HRzcQw3%*0?| z@AgZ!sS7Ii6<^jZcQ}EiZ~FAVcwgK%;TbK{O>yqQK%nYiJ)Vl(<0hW-<0-Y9WBT61 zW0@0u;V$D1ql+8N@*v>K{UW!!^=rz93Er`$q>4fL^Dl*y%gqD$nF=MhGB7Qy* zyt-%s3&^F}r{6t$t1+=Q%-hs&1j!t8FQ(H_b|$}pfj+OyC%iA7W6T`@MmWAm-#M|l z0+YT2ed(_EMY#{RvtV)=Ts6k+qQT+2vpPBfDpQa_(J;U)^0i6 zxzyE`J2NvAl7$qM#w!?xBnvA%=PYdzhZs-x?kKo8+#Y2+dt<^YqW*CZa<_Vf1i0e- z^?W0v>@#1RF~EZ@8}auBNp6i^Af=_kbdjun*$9%5-8F_TJRl*@Ykp*l?1=PC&9P0; zke88>SDdS3hzw>PBJp$}m;k-u`y_P|WEfVmqQif8R$}k$+_*O^=d`>FV+!FVI5#oC z2#!5AqzFHI?HAPFH+@P(m@jam_Rl!N{sTQn&rb)yktGYoH3jILv_-QumkpqKo+v`d z7a8j%6~%I1)T8;Pkhf1_z(ALp)ME_8*9QuQ(C#;TQ6{x!M{so8i?f6Osx;J;k$ieA zZ#oS%E{+$3wwMeTO}-`hs~h1M{I4d$zu*7c1|ZQ^1+k0YLe2l`RdCn29?r2HJjz>! z@VAU(I%nxLxc>dgt-j%?K3qJYo@1gtG}5g`Ibb2z=T4D+iFjpD$4v*Cl)L>8t#e`H z;NZYLBb;NGvUy{n&M8&c7f>-<7*r=$X*G?(&(D8Uw+%>5O)afxcx03N2c+E~Kz@0> z;DqVw>iUE@zVxfv1$tdCx;erAmcM6a{(XiTDaF!?iU!IiamTxHm=XB%`TN`d52=lR z+wA{KoulW?M}`Z!f9QmjL5tsnH>Cdo1izJdo4K;1G-(k$L9_TueOcsGv^ENi=%ar=6g;%7{0 zMVKLwzYC2>WNhPABTHNj>GA0%jhA!kkH=L!Ijbm2OWYxrJHvZ({(Q3och43@v~{HM z{`@-cJQ zramW2S3MZ&1H*mR16D}S*DMGpPw4TD0uBu&0yTl#0w{ouKCr0d8=JzGHtU$T&!t}; z8tmd{b5O=gV3mzaxn6b-en3Q6i4cs$xFSsGR0yG(nO!O$p2!}xFR_0WVb=qSfDs{`oSoML-Pj%L@^xn_&ioa6@&|A6*X+}~0j z0U~C13Gr&0UUA;SaidI@zyMBi6drUQ&d=!#aJpQmX*xTdZW{!mQpf!hnb-DQo?bY6Yft#*1v0jYO*)F)@T`PI2JWuv=)ri%Xx zr?QH})LGP0fOLJym#>&^TE~>{lbQCp^Q-s815T*%~>1 zeCc1vCYU-G2rWA;D)V*VftMUK?hlnvO`jPk$Xq1?Tj(Um2Xk{A4|`Z@ zKTSQ@JJeYuzf5k{=MdJ7ZIh3waOVYqR4SgH8g{jw!Tu${OV#KY<>Kn6G$L#*ist`N zW0rhs1+303Y*Tls`_?~OanD305y-*%E^JvgdAQ5&chUYC>sMIKJlyyAoTRlyuaY6s;>>HE7K+sJ2wYCdzfFQ5)rs~avNs6H-dQk zTV>&oMtxuHwrFq$!!n)=>ct36tnsX_Cr4pTsaA9dTQu^URS!!8x@9mh<&`RsXP% z2W?pe{;|V^*)V|xE2bwBmLY)u4rLp4*w24hocD@`;dc2PD7QY?(c|1sWIG##E!30H zSI2kZa5l|lXumNP)!mK_Z2(<@HlfjlD+wVWx6p~YYQgL(N zE=lRSuea&Dpmf30PE2U2OON{5izi!-=?{Vrqhm-x> z7~L4Xun%~CF6Bub2zUMo`Hp6p1d)NQSX-*bqko5UTP6(VnQh3AS&YaG9GV&G^nqrU zdyso6FbO|>eD=7w&vJcu%|q7zvvkzpr}zOB@%bG@{ewO^p)aBfhrxX=^2ug2{o!m? z^T-%XmPckNHamGF4<2;<1|g%RmOMznVAr7XY}eJdiJjb9RNIFcl+sA7lBm8vG|KV} zwAU6c`0ZO9HOxdz^CRS9IqXoj^-bZHl?d;aKyX5+W9iUoS3D`ejGzb#M(91&K6RT= zFRj()2Sn=%u{PY!yB?c$TM>S5DY1<%M}v_#qUU;=LyZ&qW1^(99n+*yLH%S0ewJ?w z{2~*NK3P34L0VweF~Iyu7fTy)2}C2<_|&t`a{vi5yOrw*e`Yz2=Rjt5=9;#eOz5?D zxHQ`fo~_9L83Ms=PxkF4QL~g|Vv&RmnPL~@1Nl@dfU;{k(iD~m&F&lOQ`bYUhk}&{ zt@1Sn6ltLr=9Tb5PA`YmhEOC5s1J?-jHyE9n77LpF`ZpOTxEz&N&#@)z4fPxgx4XV z6V%>a8*`6_@d$4oq}^*ph~D`{rEL1n0dEC{6vsiY?;nW{>M5X#NJkDQ^d~*SuLo-A zw~e1s-&MLUM$aM`q}N8*uCZLG}FD&nmDu$p}{i|)6rcXqb| zXbo3*Q=qT80#T{dS2w~dmL7|v!S9#Z9odDcgS&Ue@$FA@LCOHFX=}5@W1|~4>rQFs zbF{n6XZ_A<8 zj*K2Nck}7D1lIhfC;^`(XLx*h8@!B&NH^?Plf3=4XXC2^*WUX!7E+GofkKOb7+5?s z89poCXJF{(3TZ60jTfEl~WG$aXjBe8b4GiJ1p+8w@^Zw!}E6Sk2hO8m+Y;TFVCx(9a{3D zFjx9Ag0-MBZQP!4`J?jIX`Io1nX3rhMFBl-kQKM<{Rfz@S!aL|8^0+s=G$#Zv|)&@ zV}t+P4<3Z$N{Qfka(Zm$#0su4+4=+X0CzcD#pRVW zM~U6Gb9MWRc3i=9&Q0MVAw5O3Fy^Czd$t*J_x9B*qq&ccl2uB`2z#EdMXPyZjGYwp zJHaX+VlY<(eMojiOMDl9+{sS|3Hs;t=$^wFD4UKlzfE@?tw4SheM>BEM%tKpN<(hG zo3cD+Q_}q?2QuqQHCBy(cX{hct_GV!VjCDvuTi6ZRb%))J47K$d~ac?-4tq&dM9C| z&3PR3wWZ%8MMJdUTu()Al+U_R`}s%r)o`VR)5K~D zwb#5zJO+#|mBxfW(i)iIcV4+h5dU%SXCg}(=N_OTHzhC^&d^gsR-Rb}VM$eeeMT?IAs1Wd+ugPafjoZ;Ko z_wq5(jmVcMcrzF-@O7Q$CPu*1+nsM!>96*ZglvkEZ-^jtA z9Ff^X(r8;KQybA?<4TjOzTd-a$mM0FbIlaQOZtYK58p|Xz3Vj6cq6W+^zSR!;3Fug zed-XsIJ344psW9gS|QDNMQc%5g*5VqDzQ>)gF4Fg`l4`UEMfd|wr-=|HQ-or>RvqK4Eu?d;bcA*Y^?Zs6S)CsYc{kZf`4bw;!kd&7LT|ImI~pVr=*d51$yB0YHbySrqie6t(x8%;w7!26ml%| z61#a}HYRNm8R#Tr)(@t&GUM7UNIbolT%3(#?;V?;;^^><($%u6Pp9-$34}SLAtCCE zH<_6j-YBGsk(3-Itp~}2POdK5cBcjyQ*(@r|BxRQYcgWSUZz}{(6gPrg40zzABJwC zZ|j$D$?KnuNTQq_MP%<<7yLfjnF}Z#LCGme>Wcwl#)6OWj?#;rZPZKlJ7#ACi|@cc zdrUPfq)o#Aw)B{*2o})4JBr#`kHmn%&8f7_SjQ(J3qHAnN$|ZION|V0+RI=1;_WK~ zmtvE%#Q(~jBj^$NXo!yO$zrI&$9*qjPsySTKyiKEjp6Fd^rj3jEY|7gBDIsdGy-ImzQ+H4vTD1Z!vQNjDx@+0NK(hOTKX%?XUF=c zUR>m2R3ArnUV6sZ1KB`aiB5XnQd3po$;@D57gX0tj`V`QLenJ_wj@rlO&Y%Y9vhYT&Nj+-f~j>#KEcxG6YIy1&hUS4h@^aio=3a>DjoJZf`=cjb%@2 z3XcVU6n+;M>lmV=;7@-vc_m$PnMYL@k)?U!YR>#c{6EHD*c@&Gjm?o6zb^5NBv)X_ zhP&B2IE(~OB+S!Ss{vwj4^~g1AG&T$?y4siw;PRvbhvrVEi8V9mwxGBa7b)rZ(IF$ zCK4RNwl1xsB?a(l%MKsuT9&2ADs539za^F1MG)rfLU-lY!>wO>3af38kDcA}J!~nF z#_XdWzeKR_j|{U@2MwHepzjn{|7n-`JseY+ppp)p>3Ln7 zCs*%D0=6da3gIhQR`r)AYy&LVr#Lm}Z=vdc53>FY%>Q^`!vGoGEaPzampL8tjI6_F z(n*tBQy=Ttho^`95d)?xsGS7)RXbLpgN{^Y);TwHC0BL0Kn}3K6l`|bE z&D_}B{4;SHcG_a~_Ap`U;b+?*Xru2O3b_6p7hEu&PGLO7BZ{f}xti zf|PeYCw9F`Y=f_dD~}L+`MFzRakm zj7ADYq{Gaz#_!aFtH7`n73q{RA~*cj;P#If5CvxBkRx0#ubwY0YL&E=v$~+liq*qVFd(e_H#*c;Cf7!5=$_d zNt(W-rg(N%Q&IdJF%WSbZsdVC!K|3xJL|Z!Z4r>IFx{_`G0z%w%L{kSVIG2+gj-qJus4_$luxK9V<{N{ zK2Zm2B(Vfw{9qjjBf5MgP-fP){kW*Y>Z7Dq!h;3Z!S;w}A*F9^#16ne(BXvPI3H0AUs;`3|F1 zcATts-oVWpt6cSA8Q5^$pqqqgtfWk$Q5o)!tgi7JX*ll_@ zBdnxo;v}Gq%^Rd16c~g{#v5A&nM@RxUo0K_Z^n1UeF!KUQE5%_kFmb=d4lC&Ez)Dc zi!f3`5w}HnO5Oc>!N}j|@n)h(xG4?7kzjO@(n^@TFa3}f8mGZAd-Kpqbwv_&DrNXq zZ-7kdu#9{_?^DkR3}i}z;*m!ge=qGaE!>S3uoP9ENws)&_4whFDf1n4w&9$$;5Yp8 z-Lha>-Bz>+9kB|{^U*K%v^iBa5#%I#Mp-u!JMsszi%~O0qfW&saQP`el0tN#%gk$2%WNM_8icvJO6|zAg^F<>09@*tyHJBDpTs z8;UOg1f!}|SUePIvugek@#tpJf+@#SB>X_Bv}iE-mkSVUzJU$U7Hl!4b9jh**T_>l z%!0-|+z7o5uZ8x+^4;n8t<-RIO+WZtMgouyGFwPNmUM(wuWHb=*5lSeKK z*9*@|LFmmha6@EUa(+K%yVZxUC>&4Q!fPG9)62$%I=7qOfuvhzM&n83pDu4}A)zQH zB(~eSk5F^4GR{HYm0p^$z!+Z3-y9 z7}ZMHzdYZjz1gfN8GF2?t4Z!o4`dh;_|~D7$cACP4TL%hehV+jJeA?D*b4-_>Nn!n zZ7(-#$~=c@Hh>|QZtm7AOrJ61ADZZo%vxitSQG9^`_pG+PD8xy7(v!sU7geR>oO(H zS1`HAp&HON#wG=GwjD#k($tla&A6}qIyqEgea))&>cz2*iI1Hm+|I_5F9cd^On*OK zQ`}mc4cX}zSuRr$-&k~~jd~Q-xV`n{9Vr7{6?5tGmqw}S*_a$3xz5ei&n}PlS|MSK zOuL0xbA@l$@A1HzR0&I9?1CfDac;=88skOP+HtxOdaT9;+cuLVK!33Lo<1t=p1aZU zx_SI@F#nzR-CZHCV%Np^zWeRZrT~~~hT(7rpRUKl<&f#o!_4l0e6#YQ4&$<7*Y(l8 zTNd=%dG5GA;4_QIarR`>!j1I$W6UUSpc;p&Cpd}Vl^vW$82oz{Z8 z=5$J**XHrQ`gsw*dtpg>crzqwfdl(-&+ajLCtNvdso3p6`2j1O>Ka({9@P0MmZ>4m>1E0N!4`IV!OZ8ZipcaLax9PPxpy6$?m}63 zTEq3&?6@e`augvR+XqYzWc#d;wM^Xw(M9Kj#UU&HYQ=*k8Q(j=?VOf$3LHxRD`Wxx z*E#3<+=oaFvk!AVj_~k%`mnx*#2Qk}uLkssnhBgg=T1-G*hMcr__0f~bQM~ds5jP@ zm!NyTb!cX0u&Tl=bb?ocX1p4RcE0^BQypTZOT3d~XNDh6kkBNv=n_!?T%W8Bb#bn| zJxIK*Cs%%gnjJ!`c0*hX_UXMDJ z#yT(L3^T0}hQFKmJg%rFdE9Dy)mR_9*q}#i0B?Z8;FzCZ)Q(C%wux`w#`D&aq_l{~ z2XB=J!w1=CXMAZ=ys;$e$M3hF-Ykwv!)CRRqlmq^$4-rI#;!)n=hU`(<)zh(zPCcX z#t9`5s`F(&t&r}6abL0VA+<~^G{U@s)eBwlwKb}DYS)=t3I>e~kb)Jj@#^3ptJOnz zkVR3zkah={uBOpRKP$8R>?Gab#JhSot?%NMANnyj8lXx+T*z}FMpErqq+BF2&LD>E z<;5o7?k=C${F07)=kvqO%?MJC$n;N2G*0pInF zTyz{>VN}TO?YCo1hiJ|yPV}W;TP@jzo^-8M=eh571S`^*kG!v*t&BeQ=c!-jo;wz5 zHoKIPOQv7jN~8~sVV9gXU5eTq0~4&8FG{@ZhS@521^W(wt4gX>Q)N4qd)Ldyl84IC zQ3-~vKbx8Y4D&;9Z`A{JUm|ImKP*b5dpI)<3qh21y;q5Ua6`jDB@G~S;k!3X^VMVX zvl0qsq)kZtZwQc(7jPiy7#QH{l!p@A?cHv`+BT-(`)#WR*8AtGJ291%7FtQ~@dEK0 zaggS&l^6*)v7XkQh4QF}xYo@~H6g_QA+MVEoH0M4!?YLPcf!0DWeUNu`n~?mt3W5j zL~m93AAPmQ0ut-~nca%Ascq6o^GV(xAC&fr3`N8_WD*5>hI{Ng6Pt(1rJpI+-So$L z+zQ)v)=r!Db`q9)tyu*apnTy2vzG;iCoY_CoiAOEqS@K<=f7osOer~!cjz9;EzV9% z{zcSU1-&<6F6K}05%vUyd8u5G?U@aOU9CC%OW!ZpTiDF@$;c@ltvkPaMj`xN0HQQ!~0}W%Wu{a__bZdLgev2*FV-`qSIgev= z!=mGp$xIoI2p-N4#2|}R2%^oQ&BWwXbg%Kdr&Y>vfep4E2YE9zq#KUn4?CO|sgWDG z--c94Z(tg47pv0A6Y(F+UEa&EoSuQXtmivWyg!Mgv)8nsl2UB84A~?_i<@}hr*EIn zS>4SpPf2&T3dQ6Txlohz7{4uDcQ}IxlpHN|cFX_hIY_1-otzYR(Se`+$5$Nb4PsJ)dnD*%dZxQ}?02CM1L zE3|U4QboybtYaF_KJf)tMD$o&F`N@610(Z;TN@>{a*)^N1v z?KEiA5k2&Y{r+%OoG%0Z_%2VGTmu#m`71R?wlxPY>W;SVR+{PY-eazZu%#+HQOAa)}rR32L5;;T+i)3tM{FBC{=LkL? z@{rCyeN^MAz!Jai7l=2WP$%=p^{fxPoN9^mRG#3kkE}NhjK}i7PPufkKnzomLB9zm zO(7t~kT*$s)jWjnBcpON$<8zJ<)lU6>|8Z8Z&>w`TO9O+T>DA4MRV+09W0-5_%a^x zB5`ca3#l(s@^()ViK#ilGa1tQD6+~jVQ1;lH!#BH3HOI%#^1V>UO@`NPnm46PM+#Shp$BwoF0RaWdcS`C z`al-_3+)HcG~|bIhhz+ZLi8yFYj4{haVUj)B6?35!@iJxCu(YFWXS!_kS#WaQT>s3 z5moP&*f`aRj{Jb&OqyvLysZ+Kuztss2RW#cs>LMDwK$G2y?to4-0yRuh=?pBL=i1# zvvC`8d37~8^LsFn)nKmDldpO#S>9ay{rG#!uPeS6!DM~|6~S5X-7Ku|kbY%lO!)Zt z!M&H^uXZHfij3p;U}UxO!{sn@b#6mL=y*RYX<>BXL26=QUJP$!r71SOuC?csh0NQ=&;%Rfliv%PuMZ&XQBq&2<^Gpe zg}|<@dR)>7@UmEwwk*gxghB$_EtU&`c ztRvk1T$%b`=co$JVvjB2Wz=C59fE&uDUIwFSQi-Ukw%&J2lsA*#8UkPo(|ovR5y|k zwaCAXM<(#tUz)Tii3YRYW;{_jRN%%+_l2KP|r$h z`ENdK+-??s8vJA&k4@=nU!XOw>4bLS*A7mT$~5}@u{lLW6_a>MdmLaecy14xLW!bC z>_;Jyu(h?&lc%Q)h%dQHCG=~hC|wd6GGo>N8T`i)8Zu<4E>be&H(jI@$~vFOl!fcC z6%;kcdnA=XLRJjmZJaOfRKBt0DAW2@h_V?fD<{2MUF`nAB<`#$wEoMpF3TC-H+?sH zc$?$LKh!ZT2u9{h;Y0hH%vG00n#9yyi7DEPS(zwH%b=G8`!^Pp2RyiOoX%1a0?!g0-q{>@9{<4X?CgACi}^2o&b+x}iK!ad~|6sxhet(O8 zi{Zo{VkwNR4ypvj>nnYZ&!N%@lo$1`OBmq7W3|G3&^;ha7{-$C{d05zGfb8)gRYp9 zctkE7+`DE^j@%7u;k7ya*x8*$Gg14W(K`34alF)fE5g7YX(}G4^@bV#j26TH9c8hP z`EAcraDql1micOTWAzJxAx^D-FB!O&96=UtF}&jxei@=WlqHQ@_}iu!It)4{MwEJk z*9X11#51-E(&)Ovf}-`tya*K)@OlnroEF+DBij#V%q!!;>nlZNL3{(6N4#jrytBF+ zB?era*AkWAzD#iXlChQ4)R9mG`+Nzf0h3C60olT>`hVjU9*M-L#^Q)14AAf2+Z&Ii zEvC@#4~7b+p7iI|qhc*Zo}(@K!UIBRfyg55g8xYP=8y%VU33elk`ZYft->-0i#pu+ z5W&FZxL(ubgkM7Z=A8x_Aw`W+;u%NUvZqU1ls?FB76$(}pALQE_%7$qSJq+pJFy}} z0x*Moe<+P)VaHdbfr#G);-QoGT-o;!VX-5hN)sznrS+u0Dc2Lnz*~e`?7Z=iVHZPB zgoZ@tonwVV_WR{5*++y--&HB|u^d9tf1M%(87s=>RO2IAUnH@xtSriC`C$)3an=|A z1-b;~eS$%-$0Is&n6oxMB04l=*}*>K#edkws;TM|19c$Ir$l}KBX9*d$uM#-o&wE| zO*l8}Fd0KUNLvW8+>PVzSfoKeIAa{k{|BwSo;ufeb97{ZM*(Cgcz(Bs=F=y#I>Q*| zt;$1C$bwsz3*KkmPk+RFEW%{yBn=D%boj^GL_VR`+}NyrU@lz1e`bj8t0MHxOlr@j z#SQBxhPH2Zy}edlT5<3pMq>l#p9av!p9MszdBSJ^8Yv1GZhRS#m7_FHtBo@p-eKE7 zQ~cDwspWb%nOc1hJ7(>2FeXRbwtv8Hd+pq~-Ewb0)150C-rxmnS@Sn`a~pFbGEv9| z8qkjmr3gn~tO$98s%(&DgY{A%MYJHK55ePAiJP97pAl{|Cka9&%BpLErAIEHDTf{> zX=%YA7IDe*xbm7rKUrI zC8uTQyM-DwW2ZBF`k5+=g2>ah%(K8v%Ok z)w#JTBEHfqMHL(X>9vwe7MsJREct~2Bbhkl96OihtG60St4_#y6}OltisDQ^o4_F{ z*$JG(HBrtMPDm0}p$b$mjT_88Y}L2@qC$N3(DP6`vx9va`2^d7HM@Z;JR0o5Z%U$p z06#I3i*cMbA2Ci%Pq8=A$!-NXY~2rBIeggi!{}aHP6&jGlgb4;_r_v=gVnuP4<0)B z{m3>_Ldj;A?$Mh0WS-aU8!Up}3|(8EappUwC<~zkW|=$Y_*omK`VyfVv@c@mb#n6U z9{`^0|L{HxBU+!l2AO}dN2aHeUD9J1f<3FOE_q={iY)|s?@Q`8x&ZT(%M@5eOPpaOtg6iP*(kF=-`j#y#T zELp)ynIF*cb0+cRKs=#+3v`&7;*KMFeLZI*sdi+XdlW_>;x==d-y~IU>G1dvlZo9! zXI9?VMSf|Bk=0F+8s4WIX)nEtAZm|AFfgTcuTo}WDDue5wwJ6PFmJP2+&UKE5tuF${xv^2Ga zO1$|Tn1b7Ob%n@v=#@(UR;Rz4sQd+~L(J-a|103s8;QX|tKJ-tah|$yAVPC?I6qbO zlU6@zeNhp$G`JBKWzTG% zeYz(bgp*`;UGHf7vpR|;{;T-?Yhr9ec(HYlUwdQ9=_LFG+o)Bb|4YGv8o^rx)YO>(j)0W*v?c+Aq?#h#~$vCY3Z#*#)jfWXxl9Z*R{_B?{ z#F8qzP;QIW*|A7ZONYyEw773J-9S%f?mso#$rhW;J8c&qewiK8z zksb*&~5fb=CZ}UGs3?O*jRJ)%oJ8NPWZ`_*O}PAOmi;Ep=gwpwBJ;=t5kQ&CnG#k_%(U4O2p%*=^J)fvWT@@Ao~y=P9E z>FfTEk;eD8Dc!r4|9poUE~Q5Ip@vh|X|lz(W-3*GpDyjT;QV#{D5)rgr1Y1$7- zPUc;ZptNR?h-yS9;>sq8j(vv2W6|9M#($HtT6Ow}-lGnW%}NK~U`{wqvQ3gV7C1D$V9^@gYu>#d3S z>@M`BDcN5v2y=(TO-4s4n>`74J?ao6k$(bi*H8?s$R+2R8wxYuNv?2aMp2PNs(;?-VWPnBoKRZ1*XNF;LEPLk4r zSsuw)`#YkfpvC-6`9SY_j=1$rHWpdKMWH4`Fg{_#n{L1f)>&>eQWh1PNhGLNU3`G6 z-K0|D`pRL&acP2(l|2TZyu?t0w-5SUS9ksL=uVRNa29R(Y%Lg$ z1XM!*u*7@3AP{12^cj*hZ!CEz(0yit-;&MXd*x>S`J;gOV7kE5QB*oeAYoi~Wyn&< z&8wos^2UA^H}Xt?XDX+Y$mU8KV3l*hf6yTf6gi@8I665p0=eI>P#e#vP#GTE?#`3F zysD}gV)~AOW{M32M19eva11;_44@iIodK5rI}k=DhVY+F9FK)&-onzd{Jf%ckKf&L z{jgEw0Iogt4eme+2@mc-LqK`M9L{Ze@35hI75APClezpSx)`WWVSt@1(yc$y03&5+ z)=IOpyMDC~oN%aL4p(_8A?P`e_&MXf*Pow?BbwZy;P!j_F}%@r%ePB5M2$GZcOzZ= zWy*uk4x|`pJgUVPvq_r!Dly5ge572}N+bo9g z!Y$Qh=eunE2=Zi%EsT-a^I9k0Y#i`--`Uv>TW_!<39!YE?_N~v4YRi9+m75;^By;> zE7_@)uBTi~+#3R-#uZHr`%`q2uxMnBy&63L$e(^4csjj6FyCCFC#hF?s)s|!f$X6o zEjLl1Rpt-~>m@qj2lHf#vM|q{n)Um+=wR;=s$83z-Drx7y5xAd2ccOPyWmA0JIFco zFY=5Bu8B9ol?D3{HmQ}QC|JL)J-~FakRpp{F;jGrhJR$3Bt?9KxveT%h^Uk;-&17O z!7k$qFVhXZ@|4F4Sf?tVKkvWRUl0wPHs&3L2c|%@QwQQ3;5L$5P_&4G9W4%`Lg=3} zSekkn*i%|OT=n-stBcY_TQMg`*}qbH8Nm`c8(%^|J%ot4Py&`m-yRC;SE$9!V-o2%pmqn!yn|IDa?@G6WRNRDUPOCE~e0$f&k zfdkvXhWCO96qxxPNd1l07YA}>0wWf320J6)P#7=?59Sv!_^rEb+4G$9yk!sma~4Wb zG{jN@jrtB@v1{~gg;v5ZXIkle8l3a3i=aPaI~^Biuy!CX30A%j2A)K9c)u5CBzHTV zmU2EBeuCk8myWldNY%sG#R4^%L!Gi|EHChB8^M74p(pm#A}Hp>08bAeRuLOT7PifP zO`0ke|I4d;#>t@?gn`GkKNJ&Q!q{IW8y*v-jCk5y?b6D#F$islQp*j-L$nI{>g3~M zvVnqq=JA%^D3(e3NAUcu6$W7cdZ5Ai6{)~vABbbrW8G#7J9-L`Q_1}-sl;7%rroyp zZ$r|TKzH#6K0fut;x)P5-wtNWE6=dRVCAKLEL+nDapmb7uyAkQSn1YTl00a??sG0G z2>sVXW4JNkrqQI;l^PU+P$o}@OZ{0@k~m7#lCsxt*`*hAexTn@VBhQ@7!}tR4(cA& zN$Gu7vq^e%g(7ymyq%!RtM=(tQYFOo94($*^T7`u>p7hM1U& zh$vza1jCo=_Jcuw@1txP2(o_Agrk-tat0c~MFB9IF!2@su|_alnX}pP2;V{p^{~qVf>|iE!uN+Axg3MfS1TX*SeHEC zjw)JwuC}8eg@v12h52kwkKEUX=DqA6p|B7$<@G*PmG~w%VdStGdOV%Hr1xQD2j^mn z#tWNh>doKk`|sYNe`-h<9}SKk;k#kB!NilYf&hIn5(T;WD+X_J2ffMA2YrcmyoF^2 zh6-Trbt!3)Pv7Q}6oq_sZ*V1zhHS!M?s@qJupcu!RJ-VI=L~V?UOsS8{U;Yd4!OAm zzuqOd1xy+(p{y$8`)#5xEGZ5OZIMe-C1>zDfqO24oK03P#SI^(-p4d3k?Pw8%o0ma zNdH;(k6%{^M<8KnEGpTJ*!;1Mu>6iI1TB(&u8<1bHyIx*Kk(!BS22Y4lGRxnsN+sg zPvcAchi2_^gq*%_-Kw!MCBywi0xUOf-r^0Q>Gh+;;NXC^Rgp!B4Z*71M$uZDko34; zaruY7BH-_Ty^KEx`IshA#2^d}578DEk9*BR6p7{tPvX?3Q$xd7+F&T zSl8S=(cc%IGfe;MZ6j!#R_SUwj&mlaq_nHBs6)PQaGtg53|d(Bjo1^7RES-;D9oAy zA&ve$Mj*)=I5bhw2Ltxaqh8_<47U^VaEPLVCt?5@7uBFB=441^eYM|9 z!-Y6Q<00Ggjl6fLTZ{}he^ZY-$nGgF!I)+X$>U)s1=tM~jdI*@ zvrqQ6O`a;Y(@`73mz0z^>R{n@^BcVD8X)H?u(W!g?$)tR;?KRGG;0JrIecLSW2l!8 z9yCZSc&`VKrZJVpj6;|y%5uR!J?uygKNdJ;3 zWA87KYGug!=I*72MuVlubGm{HI`Tuwz}U1={B+;#WH#a@9Yq)wm#E_9^m{iF0|T~u zSS$$GF{tAuvG9K|V(*O-Z79@bd~Ew*Da*K5B~{nbl4h*yic_2yEuJyQeQP`sj>1KS zjPPXh2hZ0J?dP~r_2XrJRE(T4u>Z|#yeJchkFY&<>rshRDv+iOqga95O!y!ILoob7 z`aVaX$F2M*7OJBkl5;@yyw(7yI)lxei7)Y=xMtWnh*by1%H1KDOT8MDF9R z_j5ihu*ub{(@RwF2g?^tX|m{_&CLW5-7G9D@KQ`(ptB96_DuzEq4JhT%s{-;!WXWf z8KOl5G&k(9PhB1Ttd>PX^aX`Q6u8cRjz`ZH-|A)waU^nYIuJjrVi6>BZR-1AU_e8% zc5rkey#r~15d9xdgM_{TFUJS{E6?so4cYK47W1mBV@XeGaiyo?RZrVb+5s8ZajoX8 zcuCIR{q3+vG7g+;y4s%6<_Cu$5EWX5XjW7d+`oy9M0xF4tIYZpc~B+rs@-a%eo+VS z{=nqBv*;mGC!0(#G8+!wOsQu7*C$NB-RIX5C<361Q1aa>4x`uIMAI0qG@%O zp5K4f3^Wm^2|OWVYgZ78Meux&CMT$5J#F=&@n!M#CHU>KA-Pr2`1JC^I^Nm@$8Llv z1`GWT1j{-}hU`HGtCo|_dBR|&?uay*FF*SMkU;~s(#|M2(VKv8*Er?pwBXM+<(YNq$j* z@FE?)17x8xWHNo>M8i*L{2j{jLb>R#Vc33wSD%v@W9W1 za<~p{foHWYg(n?Q>icgkT9v<-ytV9C)iF~{3-C?|KLx3I0@Wut>fPn|;N(=pc z$}@YK5wLDle#SpGpZLs-7-BPO?p%JK3~2IQ@Wkj>fUAMym9hUG|J1VhzkK5V-?#mj za?Ag}^zDECZCecn#Ym!Cep8E!#d5E!%6VW_RaF6(MwZYwfLj3xmkY?)zXx#g@9{rV z{o|F>f#d(+mj8oW|2G;4HMb9iareuCAK3o6e7(#P?#~%%Jn>))jCAAIL1eQjzJGgj zo}XKD^uVCr23Bsb3Fj+Mk~YGvfNU8G}bvA}%4;zn6rJSG47YZ6#SK$xKE0I`f_ z5Ay1A%|W4z_rN~)8>+E9FZW;%_npsW+GpI$`?LVnUuW*!*5tBRCC=0l{mme?Y>xf)iE8JOA4oLPZRi%bkm^&qpD3m`Rejg;-Pj_3~^)hOC z4gU2a>v~<`ayhi*7I_k=J5FQnFt-Hryww@wEY9#&zCHB13Q zSwd^vsp=hZeQHsa|D8P90OYrNa3i)!Md8Tbipy?q2Wv=c41eb4@*Z&ARqBL!r@>i&S=Vy0h*< z8#IlDa58O{)%`(=NvkdTFxt}f zyq}-#0ybTzJF8`PG$iQ)6NR$PYp2ULv$Ju>0bl zQ@h8>_SHvmR6m&HhG<2-Ula<=jYM`SjAVK20&P);%M$spxx{Us zDed)N#B>f}Z066iIW5xdcvc?|U4|goHXGOOhm&fzA4(RzzG;rFT_A};nwk}TbvRk* z)S+T`Tu{(XD)d;tuKk<(3#>?uM&ov{eEJ>1FvpU_`NEQ<4xtn2eSZTtf8TGWq>EMT zls=Yho=|ZZ7N&mP+sh!3Yr#^nVZW+fPyAkA;nmnr;sA26yOt+D(tqejO%@y74nxYT z&Mr){B5i)Ew@ByDm`4#${VWuTQAw`T59^nI0Co7YaL}Odu+Z<_siSIal6HpiaM_qM zW4_UjhY^pw{Si+6>002jLo@!n*XB_}uMG9_^;Z;_GrnyDp+X)FCFH}q_cG7#1qx9kvLy`rW)0`-^tAbo3e@1u3W9%k$9x>2)R78Ns8AU zm2PvS4l~wIpRGmB*!7pL>TQprPRH)r_0Gnh?_y2h>%2CV0a{K^U7KCkvk&fg{VUV@ ziYBw3miE=D$p%gE;A=7Y~`h{pofIY3m!LuHzMZ)$4IV#Qdz^}^A&b<|=m zZ6mcj0^dWxjczyRB+u2EnKLCNsZtL+Jbi@0m}qoycXByyBQ+p9vXQd7?cURh=j6sk z>{B-~Q-d4qTZnmW0(IvD=eyZrZL{OXm?YMS3IJrly@d)BS;UeNa@{&b_j&+%4r+T{<66Kv_gD08lMSWKpA5y9d+wYDKuUz=GW^ zZ?lp$5?XmfviBTY8bP_xjcj#5tWo;+hpq8O?*-~a{5KNp*HgS)*7H*1^Y%PfiTFQ^ zRw%L9N8!==xb74u@UW?(wWO+3-HB%#_jyC)*Zo@_p>%9 zJku>z&wTuz=Wp+ZSP;L9Kgi_c{ZsA+k7{t6Rg8gnWfnRz<>C`@%Ek2&HRB_n9B*Wk z2CzBg`E`5eUndEW+xsk&p91U#_j>%dlI{Q9ed51n-~V4Y@!xznVYSg`MnEfxduccL z5332vnIA_@#TOIjKSbHJy|(ssM#jms{i6@=Yc1|O+48J3M=8~WBo0`toeDQyk2S91 zd|x*ZczwZRg374g{8dJt?aT5~rT&^oi3{EEt121YRk~=4wBETMwqCpTTsd+5&(jA8 z`s59{PJ6=KA!v@=Qd8QR5_yoAH!OI_Ou$j2$(gE&hynHLduZW+5} zKp6PtG-fiQ1|^_D6^Z%l7|yEUv}#yV^(hsgoSSMcA`Ff-Tw!*ZfOP{2C2O79Z$40? z>DHz`Kx%xxND7!ev+3t!KpEw{SUab`9e#a4SFcvvxvhqsHS}QTdHZ;Garq`Qps()| zwE8_4;W%EG;=KYI65&b?Rv+z$z2oJ9&|dQ6?Y*iN zs~J6g`Lm$r(m#Pxzf8l@_cmVLn(`LwW#x>&>mdJkg9Cw{I-yX;VUUAZMX?eXr+8INM?SH?DG-R>NEkBzD9lqlg} zSLS0o7JCAZPL_|ACcBqb;KJgyh6?dW*}>zCNpC!(D#`{uH#-I|lHTJF$b9Yl_S10u zS4l3=o&l1eQYQ3hAw*|Amd%JVa(0#mmGkEVW_+#CyQVQglf+>)=0~1Uv_t)i*y8!e zEi<;ww`XZM2};%0XiN&)V6PiDy2)=V#f*{c+`eJ(W>{ncb=6T|MJ^na%Cn<3o{Q;9BlZ)-`UcnN#(FCm$c$2t=L!*ZA$kLSL zas*=6_sXBo^3Z;xriX`WoW`?nE?+51Va-Kv!%vk?K#q4SODCHB-PMm<^okF92Jr9Z z{SPz7Nd`4aES$U)y)3q#lGS6=TD_`foHuSZZ<^b5(^&!UCp=UKe46C~v*~YNCDU7Q zW51cRiEQerYEzb+`{&C)Uk-$iR+~Pd1Uc=^mpUJ;;^GdLq7w0~kdF?xotq=TMg)pG zk_gdB(lHFgIv9;CPgL1cI?}A6NpvgP{W?k5ng~BEvAuzVfM`(sgO9j&e{eE2)+e;D zGShu87a-1qlwcAqZMO0da!=vNsam|${PC(3a_`jo%F-GVRL zTs{oi*le7dvuUGq#^+ff0Xre*r1S-qY&_vJvASXVn00A5gy+E25@V$mB0-Q)_bZiQQ zf`C{`cI4Ig&v|s{*B|$+f8O|C zFV&Y!y4TET=Gs|`F$F2&MGmzO;Mw#J9xup`I} z3EBJj^c6n_B|Rn+KJi|byj(%7Q0i2Bek6!p8*;cB=@_r`yym0)^_*^#ro06ZL2 zSFY8jL6G9fFaNj8KDLd!=ABS`Q|sCLGYF1^@KQ)aDql-Zmh%^`A{K<4TURU>GDlYt zi}Xcmorv2Vg5|F>era>2Tv<6bLQ!j|JRG1h7Ab*-S<{w>PD|rBiZRs?!&n0EK)6bJun9W zs(j#Ar)#bkHG7e;3|gEzjKy8n%&S?ieT&v!e&egu`~Fy|DF^T7^S(~nRM`_Xli{H2 zO#z3j6dlu~c8r5}E^dzG5WP}nj&kQ(?j)G$cNR}fTzRNp?WK}HCh)~ylR-wv&%X%SwYKQS#6528^~ zN|8{I9mkj2GNl$v`R>Lf?SlkYR8!pzcNuA7rZNrsJrPa9FA|fc4PaGdo8^n+pA;>v ziKpSb&8C2VDjH`>ttt;WQj;!u|FE<{ihC5DmbJONd(f8a7s|;I*!PnLwR|;3IwJ|C z4^-Y|dcXLcZqBk}a=Lu^_ijPEikhi!Tvh%%a_@8exeU;H*^shKh_Y-lMi8p*@xw_p zr&#W&Zq_HJLe~@O{n)8#l~TJC#OZq2vO|@$=9(je_8cs;mBn|BL#nK5L+adv7lhBH zZAYaO3wcO%sW*-S&)HI72|#vk*a9;+1((qmqKtg}m%A?<@8zj)Oms2Z$z&jJJO2Lu zrL47r)wm3!_EXEuh`n^FnoZHh-O!zKEv}HW66Cl?Y57AXZUbdwKes%;z4y zlWyR4{5UQ3rk7|lfeZ8t9e^+?35@s=VvQ&eC4zi*GWFxMTVlXe@{l}ma6o|p{_@d8 z{%6Ae59ELq)Cf8Jn)L9=O>up_TBd?TYinyvjsguu`zo{KxeLD?Z_qQck&)5Ro&y~_ zyE1W9Pzu}-?_W9dKCIe!ya>2!d3hP1kTASCV$>*>Lc77H^YIvP>+bC4E5unhSl@f5n|>0XCj$DczJm*fo-+S$5MG}l70SpZb!t%&BOHkylER6 zO@&k{qnMX_wffRFEL>*|Jf zFU~%EP=|v65qkjQK$VUACFbG^g=N$@_YEaVO>w!A{qR8q{sEXVa(!ydEG_ z)#8y|^RiLyOaaZ}krPlXnkP+;hY%G76?Mp}et6rC7BhtA^&OyHc8zZ(KrI4*K;$W- zc`-4V#F#V(0L#DvY>AmZzDrPRp6qxj3S84;DX`&YXkKP&UM^Fh`8HM=pPE`SHKjsH zO+CElki5v3CO3cNR1NM~w@K`Ouc044K4UMtbq@g$i4@~E89Zs`)%A7c0wrVJc9+RC z3th#!%@?Kw>G}Ejp^*_4H@Bvk*w}5SX2beLoDk8e+1dPy-SLCN`RenBCnK17?m9=- zw)$s|n)-TrT3X4Zq$GY0g&9Z)gmK)~cmstu4YZz4TZvW~$}ZjI-2ju;)=rmHM1B_L zqHBtIrL2`La7!|eeV(12-N;b2Bc_!kK%`e{1$FNq9Ze6$Qac_m@c={$=w8RbG05H^ zDnCf&m}+9PUf~a1gae5Ft*>(gWq9Zq{u&}V-s^?V{6n;)j zD9NVrP7@-N*L`IeBoytos;8%>7B@6Bq@$z5x0T7pw0iVrZ(g?g9A7F;fvIS0OpS&b z=LqqkiyR(GLIPkSU|M0}qppJPSTI8dj+}~r{`>#~0|N{uFsccvHl@q^z-Xy=qQd&c z!*kU(z8yjD@cXv}n z_5qFnx>=2BakOKET=fpc(b2w9t7_f?c*)S{=yZvCwb;ly?xLer&0uR3Ay3yE=zk~K z;OU)*(LtX}m_uPN89lwUa=skrt`s|6`^;~&QjzU`zIa=H$)WM__i#NdVrt>x+<$!{OkQ5bG zY1`$%m{~su=9v~#0>C20YXQ9_5o=xjaYAaU$Gny~77te`#m|yA5STcx6De@@WEK!j)V1;)_~6B( zwvsMAW$?Q$!EXd_6)6&xJpPVENZ!6JY<5zms;3kH>Wga@YVc+9= zot6F~0B-gFy|~Di`5UWLngd@|NogXO$?8my06ag)G%>J#9kG5#?bfogveJH5c3b1F z4W4=a9*swP01QFL^xxZAxw*O5Z&FHdR^}SV!=)@Nqz6US;rdz1`gxP;fA6zJJ6CnB zv!LPrWy{P@=pVZNoSd|0Ip+a#QXDzMI^4mev`4O{S<8Yx%$7eph5#{OWK;fI@RR-S z*qNU2KLSp#WoYkP0w6LJXgJIg8tL(*R(UPFNYNEoKpat~$P=t975A~OYUciv4>h*a zSadgnnK-r0(-<`+2X~_7<$v)QU4okVqW(+4fzC^Vc#Q5*O7WHRaFNQf=STX3>px#U zOza709F)!+Pxg&j7TtUAMg499O!C*WYfl?(1p^h*K?Vht4qP2r-xi zlg6Y5tOa%D^0I(hw|dhs9XFPOr?hy->G6Y(7W~%f8WHPK`iZf9H_jWaw&SO!2Zyyg zF;4H>K(EJLwx^2}zNv|+0+M~s=@X(|qLP{4*F2eN2j(^2Jv?SL7Y&u0;${~mvk$VG zH9O{3h#ZDb_wiRqai*_2uz?l^>boL+&x}+VSP|oBZ%>g0)Kaqjwd#!#2&}PbPlG*i zcdUE38^uKbxhdzeTy-xfH@j4`=meK=YV1iM zp;WmOv-Ng|y=cI}RBBwfwZ2|k8!`&FY&GjG$q>N<{!;d#{p%AH%lZ>^dHbDDgbKAE zdrt%Y{_%qPBqJ}^nSKh1GFf8e2IX_V)zu8;5ftq zAw#Gw0!g8=&7D4XY*%^0jn}>7eb)Py*Y!NdxAP|;_0)xlErkx8XQDw7h*ki|kxS#H z%%7wU>dx%!6uxN4j`#&~FGUS9uzR1mCMfXPO(DhW2C$&7YOQ7u;KChmOiZ7Jk36dF zO_uwcFWrnoa2W^MEYjQrRi4hl_si{8D%(xVB5w>DsyVYsb(O2m;{+LX1IkcaZ#oqW z>)lV7j_?I#buVZhu$QuP?-&>A9B&DqG76sE<^LFu3SjF16lOkHd<57s5gD*3DdGkr z1x;#GEd8xRm+$4v+*Dm&h^>|=t(v{69)fsg^OUZJ zcHB0Vl#RGI=i0}xRxr(#zPxRnP9B%UO>@cJ34nfADQYd zHoIy?jKaNrzn?#>JGm>Ikhis3YHSv75}4DkmT+K zA^Wr7j6F1fgjY4QbydDe=hHUFT@h%`kYvQ>Grf*TBBR^YOVyi(!T%tKi&iGYq5Y0x2I~aU^NxzC#V5+Ch?%lCi`#T^h7b+=NoHveO+MK9M9s zOaQJ@uQnRowo6*X7mARoWYx%;+*4rzO%*EVkHq6?`dEuWAdh1XvKG`pN4TiJUol ze}hg~0lW_6v`7O8Ia$!NY02uQAl6cozNv4G02q1Tf>pid-Q67kAUHK;06-L%oJ^N6 zocn>)W_}GXTlGh5ObnG!4B6j?zs}N9BOu$sb~UXERn59I9RH1uOcL8C;20VhV9{g? z$Bmmiwy5k6y^#adPJ z4xd~BImgh^l_xs1jaWxBTKmUku#KXh8-&bod zGM&sbwXyj{NG1gQh>@X>Vx>>8kB6O&&E@A`{f+smzlY*zmA?VN6g6lxv`BH#v3VIE z1o|<#7dL7&3FviB&wGbgl2TF{iXKR`gf#R6OVL+Mo>;~-6%mV4(RHR!ejm^7NK&`of6!XU(D1Obo!z-1 z*udOydflcVCr1MF`4(yE*vpYQofD=0H{bcvT9JZLQ`1~#6*c=#a!0a=6%bA^Jrt-P;gthmxk3n;{`nZ9I zvC7ZV?vtR8@9Z1A@+VE@8Z3!q$O%!4D=L1#!T)Pq#RBG=G3p0>6@`ew3U+pOPGGQL z{Di9wyVh;OO;9SGlB%Bt7ufL4hr?_imzJ~uSbbF$TF1e$274m?{a;HCCz;(ilIKfc zErF(@;_u*N)3cQzwi)%+B(|OdviA$_8UQ5$Q#EG9%EhU9IW>IoRwo7!wN~vNTr*l`SlVCdmnyOB2<-C#twVz!QIwPO zZNzo`uRRpa5(NOcgoK2A00sjl^U&s5ctH=o_H=le#mrFgyA@|FjONy}W?Q3)0I@NSN~{OGq_?n%ZYdaP0nL zH9H!q1eRvg=#++9w@973VA;L-&kb1F69Afh-WjrO8CMDY7XXq9t82>kVFC8hQE5gg zSf8%vU$J5n8~h&f=F8$2xYeH@k^kie-L9c?6R^SSyBbvI8eg3f|6lLn>&gF)WAT6G z(*Gp~sLwwh)&H>r^nazD|J7{@-1R?j)Hcj>0|YC}1xgE80p8pWif_ioSkRECLCC;; zYHtD90et`4y8ppr{`W5ZU+S(81$8|{N3Da2GT7^JY;JL>dEc=0R$*n;g(?G;<5;2o z##DeM;sNz9m)LYK-A$p^@;lO&9O>NDnC(s#Y!_A)yJyX4-#^sLnfN8R13zRT{_7F{ z%R*)CWjhJAPIrLa@!9n3#;a^r=oqik>vVwFQmghOc!dtkteFYswEoj^O111=@cAxK zNNQiG~mlVLTjHMG6X!? zbvn)R(lNUI&!<_RHOlha?)t+gms{B&S$_9tAxJqW)Jw$|(dIHyq_YO3VEiO;x-KV{%po-}J;qW@&rX-($D zIdzce{HJNPv{+@fnBejdhY)ZuYbAvdS?|5vdM3!hJzPb#yRF{`2){@ky$nlg6P_7n zL_Tl7O!D}W`e^E`iL^eO1s!{uwGyXoMpNXDyQ;Li6p5ZZxg{)6?zyC_AnhEi@I^ox zo?m_##jWLkam$ zYw=FEKUYw33pd6NF?93C)5g5s?%MhmA1kSg(kE?6k~$8vdb1Q;z8)# z;^uaXHT&KH6qeKVwOZ}gE-IfIXV=?%KE24EJl4pgJ?}H$f0$@Kv6yZ)($)*~dRUUL z;Rt)Bo?6x}mryxeRsS4C=@+z;rJr!0rTp?2FRzQ4i`N_R8{c#O`R&UMIReIi;qFB6 ze@`@BIXRIwDRvA}C_}8KPO}r|b^h4zvIevxAKj!SGoj?pd89?~oz0gSZ}m)PuO@Y1 zsan``_am2DyVo)Gy_ez;O1m=P>9#SzNidC$+>h;k#?VDO_ErQB>x4hZ=}gqH2{h^F z63<8y9fWw#b-7+_TUjdyIliQGaXLqO3*hWG9J<}3dKSoQQPS0nxW5SR;vIBOJ3gLw zbCQ7Wxb$Y%{dmF7&N2f)fNhoy`g!XlW9CIW-8rp1U;gE;J?hKHpA>1o%XY>MV1K*= zjC!2h<@^(d;fci*otb0u?)8I9K~4)Z#liFbnhwU-K=T14Sa_97QrM=ydg_2Gc;xLE_RLU7!EAnb;7TEF-F{#7?BYT5>6&Q1 zFvD{HSF4bl-5nN$-{HoyqRm>UzZ&yW4ZG{QMQuse@919a}Cd!R%ZijgpV) z{D8_3fG)dRmGh)b1iiCq-%k6`NU`|>iPp;QavK;V49sJKGKWuTT;->KT2bg#k4u%d zlaD6OBi!~h??ebjp1$LI>twxgG!NX_yztT$TFxlf2a zd^G|~+lr4gfCt=FX>hLId6qJP^IWtHbjOF>EVYj(uCG?rfOy@o8LFAEABv0C^mD`0}4NekYLnXA_ z{|S}cvDK;99$ak`Ox50_2vq;_j2NHJbF+?!EOm1g^?4odo%ZH-j}7Ph|EIPy0f(|* z|F}|$Qg4eAp||wfvSo(srLv4HS!-;SFlMaTickp^no<%%vW;yd#*!@+vhSlTLov3o zW-IIee$sp1^PcydbDit|KmWP7x*F!0-~686@4mnH{rxoT@>6_jNf!rozF-dUi_Ec1o zG0`J-tj?mBD`sz)V|xc!)-g?5&3uxNoO;i0$BIhCt<1-?rA&k@Ni#~*8&}$Ig?75V zbL4=6Djcd_uHr9g?U{sFqjv?*`!R-}ipm!T0|eXzWPf40As%s5v5;=;IcJ^Ux?%Ip zouCWePtnJ?*dT{SYhQvk4m|SdYpUb_UoMaXkX&itVD==VvHG>59NbNoYy}Ci=fN`Q z707oEhr#7Gf*ge!)2&G(7*IP%bcV;sxmy-)p39}XmSDsn2v>glQ)_BDf6^IlDkZnIb5eM~3|akQzz zP>0lIl33oeSddFGXS-uoCyu9ojdKgDF(z1MPc%DwDm5_}Mx^G^E3b9T-u+6ZnIwK~ z6HoK$2L0e*;Nol7Ua#3%WS)l&DmF|tHBXCHTMC6dPgdnnZH9n!W?Xob8s!#;3X!FNs8Y`Nmq|0Lm%Kx=ol4u!!@ z@g4Up?#wwERmx%TE&xBKAeLf7lrCv}QL>59)|_)vyxUj1(454B-HU5Ug4U#r=NXeI zcT%E5*36)1hP_>>YfSwv;q#@ZMYS482kjpdhV8K>c6ehcqeQ&dG)d{3emwHsd@pa!69EhDuVpUSEqGpPNS1Z<}_953?+tP&7TQG^A zjR$zsy5rE!Z;glV$&HeSc_{Dzz!YIU{w-W?H-6al;-kf)b?x68Wv*?mx z_Uf^Sl*F{y_Q-1v8p@49ZF>z$ZHucd*fHcI-K*-9m}`PMdNC!Px}t4VLcu(GqiKq; zNxoi`ad$jFMQf?!o`|74Ni`@|KJ_@QyF+&|VcbY^I^Hm z$`h|2VqG+OFUIU_Vm6oDD%J4asu-N64OT(m%{nkMr8V5&-!kUn;4j#Mg!bHUa;r%# zJMb|MadJFsa>O@xLlR|3cCrNBYNF zxa*bnL=zUO5};@@KP4-<`0Et5v-?gU7Kt(A_79H)sX1Kce@57s%zUY$5=#Z8C;4(* zG~tLa#F8R~lt?l$XLYhM(=Sk|)vFq=cg|z^v8;P*hG|{~anB>?x1*We_kUCW+Ldv7 zih~(74lJ}`M?pS(#oNn|@~tYU<~9FaR4r|7TY6z6#@A7D#0X`o`-f_8^b6NEetjxv zKwYmyxa91?jV7(v&y5gxRu!rXO+2)@R}_}C`v$epSd&sUES2;miA53nW*4+dWW`hG0MY5V4Dn|?@(W>aO7$9t)H{LHVP{*@y)!2DFEIRLJ5EFVX zS#N1=MBJx~9#hJ|&Z$v~uNm+yrJO~+ZOb)h^~N3vYxPC^i0n+k_T!4FPQ@8xV?3dQ z7sJj-ad({|wv9JNUX!%Q9gkWnxOHOsV|OJH6Uw&bYeJvpGc|kXZT)BeRopvYErpE! zFx2Jw_wVIgICi~YqEwB_87A#}cTYjWKyfTdN6%QFQe7-!(8HDgO5bg~IhVK`s!h7) z7}zfi_a_&!`OM$o@-1&6HxEOI5+ga$+GoEJg$)lv>9+`7wK@1Ea6FMEi%O?3KXR%uCvovsRmJsd0EQ6L6Je7HtbTD#zuu{O38R7w)aUt zZ=b>x$XfIClIIIr48s+??iH)y6}8ma+LN_juqGYMb8#pxx-`?vh0za^-Ac(1xZ&B! z!x=42=-tRZb&y=^lKh(vOFk>o5EK_J?a8Fk%)wCsR00#K688X_`TSLVQy+%+FGxL& zh!9F{NvW-c#y+nDEIv5ko4PO190zaB(8D7S4B9>i%j{458hgb|B}(OeSBk#Nf*B?& zD+`E4EA+7QQ`VO3-4BD)!(he(uM)no4M zgTp>F)HoRHOO|yRXjFkv36aAo{@b#To4b4D#EQo2kzX6}PENY9J_{*FrKAi^O>4bw zuf6pjR1Nn~nESfozfv_YW0*61>w1&EOn+~sQj^sAU*J%9_f2RYnI|86DzU$1+k^8h zxLc1vNV~4Yw;clTpOBgZHaTpn2Y0Ob9a1^x^TI9%R06T$AfoZv>7ywhH1Ws!mV;H} z@s5thUS9IsA-Z*i%nn1%y}bcp0bCJkHHr16Qmo@C8QnvT2rXgQ07eW~i106)KL9~9 z<{q`KezQNcm!TgFzCHBq!Er>W8z68_9c1%Y8IZ2{9!yme0fR@%rv8I(}Iy|L5RI!(i0{`Xh=a-}Ih42OETsHQ?l`F6G^!3A@Jv##ZbB766IiO3LJo!lXe8dfG}t~-3OgD6a?iFv`jvvmV3@#CNlZ>g zoE~szV6YF>Pl10T2kW9fMp2}+$1&hU-$!csH`+o=BaKD|a}=7{UmMqQ3;8iX3(2uQ zawDYYszXvLW`HiV45JEf0Of@rsEw$us*=o;!OjQA0$BsLO=IvrDddtt@XtoT?*$L= z5N%)1N^rZDK`25|)kVn=Qx1c)eH#z5*1IH(g4U|x@REWOdaNX-*0|p&-sM_Buz|AJfsrq7J2t$PlhJY zUN9{+%y5!m;r|Vu$#7nCWx)AD*p_7COE}HopY&$C52IKfKMY{GSY*S_F>1O)300Q!oi9Lhc9w^J=ci_x#nay?47HXNAC-0ASOevc!m0l zsiX&Y%+}vziodfKd(TunJfv1Jwi9m_JU?yS5My6|2zPz2{Or4{LFla=WA1g&Wc9U^ zTNJ3Lh$qv2zT*Zr509vLA8k}V>;+^Yh>F%Nb<2f6pDiw_IuiOgCWagwywzorlW+h< zlM2qet@10pV*2?Fvx^9!z|T4J=#WF-Dthy;mI% zRz4QfItM!)*%TqK`QgS%Ywu08b}tXHwimZ{brh7SE)KR0WhC)hoKJ) z+{a}}`DnYodHRuB0)}BqUS7Kbv1ubZx2@@;OfHAhE59Dx`1aOw%K=>G8Esz!wp|F{=Os;F!EsD%edgirys`B8Ql&_~*^*i&TXj-+Qor!5K>^BKc zP{|-n6G0P$Y3}X4(B)*!Zb5aj+K}aREFHi(AP>QntlCd+*=&`ycy7~!yU~7)-@1TTL0UXvbf)bF6IJb}@A0!29&iy|)m$Pai676kmi4g0nzySU1gaa2|VH_9#u);j;-RGyR zibDh%soJeDkj2K?hM;<)Bq%wtH(6nw;E+kz`_k&A}ih(I_Ho!GW) zp*tPhy?F!hqH5hTxr`)8JrbOc8wT}lY?2EycdYMo+f*FcED}T2jOLzgp=SxzN^5qV^IsNA78aye7}@L#DEITW_=Qh>^@1>Fv?!jK#c zBnjGt#S)3ixFStK4Q6KMg)c$ku3z4>x^$}ZUvg(hv*g^3Ht<1ZTh}#%QWP?PWb`K{ z#k8D@iDz3N$PF|gEq#^58tZIZYWjIlG6T64I)|9J4*AJNLv?Z@TF$N7&JcxVG7}32 znF5l~>b$#uGACPxIfhxBp6kfvIWwa7!}iF``^n5xR9BbKOTfX~+r46akPS zBhzljv!9bw7mrT`u5+7jDvEE4~B}&;rDK+$ai#``e@5I4E zrr(4tb{hf0cUGS>6AIaJJmXjKUpC_^g7>{fAdg=-L3@&;s6zag1`h_aJ5eQY={2C7 zj7t{`wW6f8Ktm3={sJP^P7C#0y3CwpeZ<3uWz3L1SnuvR!Nh6>914g`K$fiqrPfCS z83WrGfCgB+V&+98m!sXb0 zVIiTj7|gv-M{PDJftJl3DEG?LZMN!G$ryYUpg`E$^7!#?4h{|>MMdxTGrO|Zp?)V& ze%C<2CfDv!AmF54;|B%@Yx<_1c1wonT5phI$a`g76whTO&ni@SsGiHwM(+SDHAM&} zN4B8It@kN}!?$l${-(v~bb5qe2Is}vr{5HT`&-=Gk-g%--nAc@Y#EZsf zyF>Z2Yf`!$WCUVlO2YY}dh2v7jot*VDN-;cD)17=^ZT|*y+x-+4l&?xDx z725pD5#=8%xmeg$Wg7eYFC*0Ugg}R#bX~N3IBWmf)UFtT>V`952&E8AsVsi*oXctm z+OhXk_(!LG^52UjbuPg3K)7Nsn2Kl2J7C}cGwNisooe2h_|7CWY2O!bnH^c3+@;(n zP-wIv6pgUXf_Fln3mGfLn=WGUuxEl#vcqPy&^Jr}l&J_+=tz=IthNX2BSbzC$~T*; z5}yAgPEP1@;W6(+)F<% z&nK5f`7IxOP^gQw4$>}9TiRRl-~uMsrhD@6)`0kpCm@=b_(z+-*8d^OkbnNMk@kQ6 z(Z4D5{|i5YsYV9)Cp_f8@ex2W!{fi?{Hz$2UAPkk$8&nbwSVgeYq-4O>Fw6Xm!rRb zNoU!_+y!mo8&PK@IQ8Z~xqMAXy8k-l$0bUJOzDtTv=>Vn-;@V^5r+@@rcR*lQEFF; rKG5>8j*!P690&a8-^|8?t1G_5&+?(SD}1$(*J+&AK1Ei!aO-~n(ePgN literal 29958 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 C)gZtC literal 60230 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%0DV%sGKrLhW5skKIIkAHW0%N;!EKe4?@Z9o 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%0D+wAYMt;|(SAYF{r<5!@q7apxB&O|1_d7>(lrGC{ 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 Date: Mon, 4 May 2015 15:39:40 +0800 Subject: [PATCH 072/321] Verifiy status Codes 404 when no such image Signed-off-by: Yuan Sun --- integration-cli/docker_api_images_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go index 543182eed1020..edc7ccbea5a7f 100644 --- a/integration-cli/docker_api_images_test.go +++ b/integration-cli/docker_api_images_test.go @@ -117,6 +117,10 @@ func (s *DockerSuite) TestApiImagesDelete(c *check.C) { c.Assert(status, check.Equals, http.StatusConflict) c.Assert(err, check.IsNil) + status, _, err = sockRequest("DELETE", "/images/test:noexist", nil) + c.Assert(status, check.Equals, http.StatusNotFound) //Status Codes:404 – no such image + c.Assert(err, check.IsNil) + status, _, err = sockRequest("DELETE", "/images/test:tag1", nil) c.Assert(status, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) From a4a924e1b6c50f0f02460489259d73468a6c282e Mon Sep 17 00:00:00 2001 From: HuKeping Date: Thu, 26 Feb 2015 19:53:55 +0800 Subject: [PATCH 073/321] Feature: option for disable OOM killer Add cgroup support for disable OOM killer. Signed-off-by: Hu Keping --- api/types/types.go | 1 + daemon/container.go | 15 +++++++------ daemon/create.go | 5 +++++ daemon/execdriver/driver.go | 15 +++++++------ daemon/execdriver/driver_linux.go | 1 + daemon/execdriver/lxc/lxc_template.go | 3 +++ daemon/info.go | 1 + docs/man/docker-create.1.md | 4 ++++ docs/man/docker-run.1.md | 4 ++++ .../reference/api/docker_remote_api_v1.19.md | 3 +++ docs/sources/reference/commandline/cli.md | 2 ++ docs/sources/reference/run.md | 22 +++++++++++++++++++ pkg/sysinfo/sysinfo.go | 7 ++++++ runconfig/hostconfig.go | 1 + runconfig/parse.go | 4 +++- 15 files changed, 73 insertions(+), 15 deletions(-) diff --git a/api/types/types.go b/api/types/types.go index 7c31065460893..2f5e085eb6c1c 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -156,6 +156,7 @@ type Info struct { IPv4Forwarding bool Debug bool NFd int + OomKillDisable bool NGoroutines int SystemTime string ExecutionDriver string diff --git a/daemon/container.go b/daemon/container.go index ef42295344c44..25254d117e4f5 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -377,13 +377,14 @@ func populateCommand(c *Container, env []string) error { } resources := &execdriver.Resources{ - Memory: c.hostConfig.Memory, - MemorySwap: c.hostConfig.MemorySwap, - CpuShares: c.hostConfig.CpuShares, - CpusetCpus: c.hostConfig.CpusetCpus, - CpusetMems: c.hostConfig.CpusetMems, - CpuQuota: c.hostConfig.CpuQuota, - Rlimits: rlimits, + Memory: c.hostConfig.Memory, + MemorySwap: c.hostConfig.MemorySwap, + CpuShares: c.hostConfig.CpuShares, + CpusetCpus: c.hostConfig.CpusetCpus, + CpusetMems: c.hostConfig.CpusetMems, + CpuQuota: c.hostConfig.CpuQuota, + Rlimits: rlimits, + OomKillDisable: c.hostConfig.OomKillDisable, } processConfig := execdriver.ProcessConfig{ diff --git a/daemon/create.go b/daemon/create.go index d8addd3a99d03..8cd3030c2a346 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -24,6 +24,11 @@ func (daemon *Daemon) ContainerCreate(name string, config *runconfig.Config, hos return "", warnings, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path.", config.WorkingDir) } + if !daemon.SystemConfig().OomKillDisable { + hostConfig.OomKillDisable = false + return "", warnings, fmt.Errorf("Your kernel does not support oom kill disable.") + } + container, buildWarnings, err := daemon.Create(config, hostConfig, name) if err != nil { if daemon.Graph().IsNotExist(err, config.Image) { diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index df5901ed02e7f..7827baa2668d0 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -100,13 +100,14 @@ type NetworkInterface struct { // TODO Windows: Factor out ulimit.Rlimit type Resources struct { - Memory int64 `json:"memory"` - MemorySwap int64 `json:"memory_swap"` - CpuShares int64 `json:"cpu_shares"` - CpusetCpus string `json:"cpuset_cpus"` - CpusetMems string `json:"cpuset_mems"` - CpuQuota int64 `json:"cpu_quota"` - Rlimits []*ulimit.Rlimit `json:"rlimits"` + Memory int64 `json:"memory"` + MemorySwap int64 `json:"memory_swap"` + CpuShares int64 `json:"cpu_shares"` + CpusetCpus string `json:"cpuset_cpus"` + CpusetMems string `json:"cpuset_mems"` + CpuQuota int64 `json:"cpu_quota"` + Rlimits []*ulimit.Rlimit `json:"rlimits"` + OomKillDisable bool `json:"oom_kill_disable"` } type ResourceStats struct { diff --git a/daemon/execdriver/driver_linux.go b/daemon/execdriver/driver_linux.go index 1766d64b631af..cdaa93af3443b 100644 --- a/daemon/execdriver/driver_linux.go +++ b/daemon/execdriver/driver_linux.go @@ -54,6 +54,7 @@ func SetupCgroups(container *configs.Config, c *Command) error { container.Cgroups.CpusetCpus = c.Resources.CpusetCpus container.Cgroups.CpusetMems = c.Resources.CpusetMems container.Cgroups.CpuQuota = c.Resources.CpuQuota + container.Cgroups.OomKillDisable = c.Resources.OomKillDisable } return nil diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index 6b418b26b9e84..3d7b2b4999fbe 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -118,6 +118,9 @@ lxc.cgroup.cpuset.mems = {{.Resources.CpusetMems}} {{if .Resources.CpuQuota}} lxc.cgroup.cpu.cfs_quota_us = {{.Resources.CpuQuota}} {{end}} +{{if .Resources.OomKillDisable}} +lxc.cgroup.memory.oom_control = {{.Resources.OomKillDisable}} +{{end}} {{end}} {{if .LxcConfig}} diff --git a/daemon/info.go b/daemon/info.go index df1c0530ccc0a..e5ccae80aa80b 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -68,6 +68,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { IPv4Forwarding: !daemon.SystemConfig().IPv4ForwardingDisabled, Debug: os.Getenv("DEBUG") != "", NFd: fileutils.GetTotalUsedFds(), + OomKillDisable: daemon.SystemConfig().OomKillDisable, NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), ExecutionDriver: daemon.ExecutionDriver().Name(), diff --git a/docs/man/docker-create.1.md b/docs/man/docker-create.1.md index 7aba222b298e9..d7bdd55789eb1 100644 --- a/docs/man/docker-create.1.md +++ b/docs/man/docker-create.1.md @@ -36,6 +36,7 @@ docker-create - Create a new container [**--mac-address**[=*MAC-ADDRESS*]] [**--name**[=*NAME*]] [**--net**[=*"bridge"*]] +[**--oom-kill-disable**[=*false*]] [**-P**|**--publish-all**[=*false*]] [**-p**|**--publish**[=*[]*]] [**--pid**[=*[]*]] @@ -165,6 +166,9 @@ This value should always larger than **-m**, so you should alway use this with * 'container:': reuses another container network stack 'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. +**--oom-kill-disable**=*true*|*false* + Whether to disable OOM Killer for the container or not. + **-P**, **--publish-all**=*true*|*false* Publish all exposed ports to random ports on the host interfaces. The default is *false*. diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index f2ce4b7774ae5..ed331089af67f 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -37,6 +37,7 @@ docker-run - Run a command in a new container [**--mac-address**[=*MAC-ADDRESS*]] [**--name**[=*NAME*]] [**--net**[=*"bridge"*]] +[**--oom-kill-disable**[=*false*]] [**-P**|**--publish-all**[=*false*]] [**-p**|**--publish**[=*[]*]] [**--pid**[=*[]*]] @@ -285,6 +286,9 @@ and foreground Docker containers. 'container:': reuses another container network stack 'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. +**--oom-kill-disable**=*true*|*false* + Whether to disable OOM Killer for the container or not. + **-P**, **--publish-all**=*true*|*false* Publish all exposed ports to random ports on the host interfaces. The default is *false*. diff --git a/docs/sources/reference/api/docker_remote_api_v1.19.md b/docs/sources/reference/api/docker_remote_api_v1.19.md index cede2e1073dd7..6901c2e6b612d 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.19.md +++ b/docs/sources/reference/api/docker_remote_api_v1.19.md @@ -149,6 +149,7 @@ Create a container "CpuShares": 512, "CpusetCpus": "0,1", "CpusetMems": "0,1", + "OomKillDisable": false, "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts": false, "Privileged": false, @@ -194,6 +195,7 @@ Json Parameters: - **Cpuset** - The same as CpusetCpus, but deprecated, please don't use. - **CpusetCpus** - String value containing the cgroups CpusetCpus to use. - **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. +- **OomKillDisable** - Boolean value, whether to disable OOM Killer for the container or not. - **AttachStdin** - Boolean value, attaches to stdin. - **AttachStdout** - Boolean value, attaches to stdout. - **AttachStderr** - Boolean value, attaches to stderr. @@ -354,6 +356,7 @@ Return low-level information on the container `id` "LxcConf": [], "Memory": 0, "MemorySwap": 0, + "OomKillDisable": false, "NetworkMode": "bridge", "PortBindings": {}, "Privileged": false, diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index c69f0a170e2fa..c0c7e9ee58d92 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -939,6 +939,7 @@ Creates a new container. --mac-address="" Container MAC address (e.g. 92:d0:c6:0a:29:33) --name="" Assign a name to the container --net="bridge" Set the Network mode for the container + --oom-kill-disable=false Whether to disable OOM Killer for the container or not -P, --publish-all=false Publish all exposed ports to random ports -p, --publish=[] Publish a container's port(s) to the host --privileged=false Give extended privileges to this container @@ -1897,6 +1898,7 @@ To remove an image using its digest: --memory-swap="" Total memory (memory + swap), '-1' to disable swap --name="" Assign a name to the container --net="bridge" Set the Network mode for the container + --oom-kill-disable=false Whether to disable OOM Killer for the container or not -P, --publish-all=false Publish all exposed ports to random ports -p, --publish=[] Publish a container's port(s) to the host --pid="" PID namespace to use diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 990faaf6c0810..8d97ad7aec10c 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -476,6 +476,7 @@ container: --cpuset-cpus="": CPUs in which to allow execution (0-3, 0,1) --cpuset-mems="": Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. --cpu-quota=0: Limit the CPU CFS (Completely Fair Scheduler) quota + --oom-kill-disable=true|false: Whether to disable OOM Killer for the container or not. ### Memory constraints @@ -552,6 +553,27 @@ would be 2*300M, so processes can use 300M swap memory as well. We set both memory and swap memory, so the processes in the container can use 300M memory and 700M swap memory. +By default, Docker kills processes in a container if an out-of-memory (OOM) +error occurs. To change this behaviour, use the `--oom-kill-disable` option. +Only disable the OOM killer on containers where you have also set the +`-m/--memory` option. If the `-m` flag is not set, this can result in the host +running out of memory and require killing the host's system processes to free +memory. + +Examples: + +The following example limits the memory to 100M and disables the OOM killer for +this container: + + $ docker run -ti -m 100M --oom-kill-disable ubuntu:14.04 /bin/bash + +The following example, illustrates a dangerous way to use the flag: + + $ docker run -ti --oom-kill-disable ubuntu:14.04 /bin/bash + +The container has unlimited memory which can cause the host to run out memory +and require killing system processes to free memory. + ### CPU share constraint By default, all containers get the same proportion of CPU cycles. This proportion diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index b6087ff8cda11..e679aabd2a2fd 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -18,6 +18,7 @@ type SysInfo struct { CpuCfsQuota bool IPv4ForwardingDisabled bool AppArmor bool + OomKillDisable bool } // New returns a new SysInfo, using the filesystem to detect which features the kernel supports. @@ -36,6 +37,12 @@ func New(quiet bool) *SysInfo { if !sysInfo.SwapLimit && !quiet { logrus.Warn("Your kernel does not support swap memory limit.") } + + _, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.oom_control")) + sysInfo.OomKillDisable = err == nil + if !sysInfo.OomKillDisable && !quiet { + logrus.Warnf("Your kernel does not support oom control.") + } } if cgroupCpuMountpoint, err := cgroups.FindCgroupMountpoint("cpu"); err != nil { diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 171671b6efddd..d634b1ffb96e5 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -168,6 +168,7 @@ type HostConfig struct { CpusetCpus string // CpusetCpus 0-2, 0,1 CpusetMems string // CpusetMems 0-2, 0,1 CpuQuota int64 + OomKillDisable bool // Whether to disable OOM Killer or not Privileged bool PortBindings nat.PortMap Links []string diff --git a/runconfig/parse.go b/runconfig/parse.go index 4ab406980988e..63eeecc5f6ef7 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -53,6 +53,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports") flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached") flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY") + flOomKillDisable = cmd.Bool([]string{"-oom-kill-disable"}, false, "Disable OOM Killer") flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file") flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default ENTRYPOINT of the image") flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name") @@ -63,7 +64,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)") flCpusetCpus = cmd.String([]string{"#-cpuset", "-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)") flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") - flCpuQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS (Completely Fair Scheduler) quota") + flCpuQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS quota") flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container") flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use") @@ -307,6 +308,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe CpusetCpus: *flCpusetCpus, CpusetMems: *flCpusetMems, CpuQuota: *flCpuQuota, + OomKillDisable: *flOomKillDisable, Privileged: *flPrivileged, PortBindings: portBindings, Links: flLinks.GetAll(), From 1be7a10b89506afdcb80f109f323b6e47d2e466c Mon Sep 17 00:00:00 2001 From: Ma Shimiao Date: Mon, 4 May 2015 20:39:51 +0800 Subject: [PATCH 074/321] cleanup: move container's functions to its file Signed-off-by: Ma Shimiao --- daemon/container.go | 69 ++++++++++++++++++++++++++++++++++++++++++++ daemon/exec.go | 70 --------------------------------------------- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index ef42295344c44..0aeb5c1e60eea 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -1557,3 +1557,72 @@ func (c *Container) LogDriverType() string { } return c.hostConfig.LogConfig.Type } + +func (container *Container) GetExecIDs() []string { + return container.execCommands.List() +} + +func (container *Container) Exec(execConfig *execConfig) error { + container.Lock() + defer container.Unlock() + + waitStart := make(chan struct{}) + + callback := func(processConfig *execdriver.ProcessConfig, pid int) { + if processConfig.Tty { + // The callback is called after the process Start() + // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave + // which we close here. + if c, ok := processConfig.Stdout.(io.Closer); ok { + c.Close() + } + } + close(waitStart) + } + + // We use a callback here instead of a goroutine and an chan for + // syncronization purposes + cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) }) + + // Exec should not return until the process is actually running + select { + case <-waitStart: + case err := <-cErr: + return err + } + + return nil +} + +func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error { + var ( + err error + exitCode int + ) + + pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin) + exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback) + if err != nil { + logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) + } + + logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) + if execConfig.OpenStdin { + if err := execConfig.StreamConfig.stdin.Close(); err != nil { + logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) + } + } + if err := execConfig.StreamConfig.stdout.Clean(); err != nil { + logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) + } + if err := execConfig.StreamConfig.stderr.Clean(); err != nil { + logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) + } + if execConfig.ProcessConfig.Terminal != nil { + if err := execConfig.ProcessConfig.Terminal.Close(); err != nil { + logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) + } + } + + return err +} diff --git a/daemon/exec.go b/daemon/exec.go index 9aa102690f4ac..5febf083a6f70 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -12,7 +12,6 @@ import ( "github.com/docker/docker/daemon/execdriver/lxc" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" ) @@ -245,72 +244,3 @@ func (d *Daemon) Exec(c *Container, execConfig *execConfig, pipes *execdriver.Pi return exitStatus, err } - -func (container *Container) GetExecIDs() []string { - return container.execCommands.List() -} - -func (container *Container) Exec(execConfig *execConfig) error { - container.Lock() - defer container.Unlock() - - waitStart := make(chan struct{}) - - callback := func(processConfig *execdriver.ProcessConfig, pid int) { - if processConfig.Tty { - // The callback is called after the process Start() - // so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave - // which we close here. - if c, ok := processConfig.Stdout.(io.Closer); ok { - c.Close() - } - } - close(waitStart) - } - - // We use a callback here instead of a goroutine and an chan for - // syncronization purposes - cErr := promise.Go(func() error { return container.monitorExec(execConfig, callback) }) - - // Exec should not return until the process is actually running - select { - case <-waitStart: - case err := <-cErr: - return err - } - - return nil -} - -func (container *Container) monitorExec(execConfig *execConfig, callback execdriver.StartCallback) error { - var ( - err error - exitCode int - ) - - pipes := execdriver.NewPipes(execConfig.StreamConfig.stdin, execConfig.StreamConfig.stdout, execConfig.StreamConfig.stderr, execConfig.OpenStdin) - exitCode, err = container.daemon.Exec(container, execConfig, pipes, callback) - if err != nil { - logrus.Errorf("Error running command in existing container %s: %s", container.ID, err) - } - - logrus.Debugf("Exec task in container %s exited with code %d", container.ID, exitCode) - if execConfig.OpenStdin { - if err := execConfig.StreamConfig.stdin.Close(); err != nil { - logrus.Errorf("Error closing stdin while running in %s: %s", container.ID, err) - } - } - if err := execConfig.StreamConfig.stdout.Clean(); err != nil { - logrus.Errorf("Error closing stdout while running in %s: %s", container.ID, err) - } - if err := execConfig.StreamConfig.stderr.Clean(); err != nil { - logrus.Errorf("Error closing stderr while running in %s: %s", container.ID, err) - } - if execConfig.ProcessConfig.Terminal != nil { - if err := execConfig.ProcessConfig.Terminal.Close(); err != nil { - logrus.Errorf("Error closing terminal while running in container %s: %s", container.ID, err) - } - } - - return err -} From f3023a93d1a0a96a7312de441a550c758ac0c17d Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Fri, 13 Feb 2015 11:45:04 -0500 Subject: [PATCH 075/321] Allow pulling stats once and disconnecting. Adds a `stream` query param to the stats API which allows API users to only collect one stats entry and disconnect instead of keeping the connection alive to stream more stats. Also adds a `--no-stream` flag to `docker stats` which does the same Signed-off-by: Brian Goff --- api/client/stats.go | 31 ++++++++++++---- api/server/server.go | 2 +- contrib/completion/bash/docker | 2 +- contrib/completion/fish/docker.fish | 3 +- contrib/completion/zsh/_docker | 1 + daemon/stats.go | 5 ++- docs/man/docker-stats.1.md | 3 ++ .../reference/api/docker_remote_api.md | 5 +++ .../reference/api/docker_remote_api_v1.19.md | 4 +++ docs/sources/reference/commandline/cli.md | 1 + integration-cli/docker_cli_stats_test.go | 36 +++++++++++++++++++ 11 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 integration-cli/docker_cli_stats_test.go diff --git a/api/client/stats.go b/api/client/stats.go index b2dd36d683482..332b78003b4cc 100644 --- a/api/client/stats.go +++ b/api/client/stats.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "net/url" "sort" "strings" "sync" @@ -27,8 +28,14 @@ type containerStats struct { err error } -func (s *containerStats) Collect(cli *DockerCli) { - stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil) +func (s *containerStats) Collect(cli *DockerCli, streamStats bool) { + v := url.Values{} + if streamStats { + v.Set("stream", "1") + } else { + v.Set("stream", "0") + } + stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats?"+v.Encode(), nil, nil) if err != nil { s.err = err return @@ -67,6 +74,9 @@ func (s *containerStats) Collect(cli *DockerCli) { previousCPU = v.CpuStats.CpuUsage.TotalUsage previousSystem = v.CpuStats.SystemUsage u <- nil + if !streamStats { + return + } } }() for { @@ -87,6 +97,9 @@ func (s *containerStats) Collect(cli *DockerCli) { return } } + if !streamStats { + return + } } } @@ -112,6 +125,7 @@ func (s *containerStats) Display(w io.Writer) error { // Usage: docker stats CONTAINER [CONTAINER...] func (cli *DockerCli) CmdStats(args ...string) error { cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true) + noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result") cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) @@ -122,14 +136,16 @@ func (cli *DockerCli) CmdStats(args ...string) error { w = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) ) printHeader := func() { - io.WriteString(cli.out, "\033[2J") - io.WriteString(cli.out, "\033[H") + if !*noStream { + fmt.Fprint(cli.out, "\033[2J") + fmt.Fprint(cli.out, "\033[H") + } io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n") } for _, n := range names { s := &containerStats{Name: n} cStats = append(cStats, s) - go s.Collect(cli) + go s.Collect(cli, !*noStream) } // do a quick pause so that any failed connections for containers that do not exist are able to be // evicted before we display the initial or default values. @@ -149,7 +165,7 @@ func (cli *DockerCli) CmdStats(args ...string) error { printHeader() toRemove := []int{} for i, s := range cStats { - if err := s.Display(w); err != nil { + if err := s.Display(w); err != nil && !*noStream { toRemove = append(toRemove, i) } } @@ -161,6 +177,9 @@ func (cli *DockerCli) CmdStats(args ...string) error { return nil } w.Flush() + if *noStream { + break + } } return nil } diff --git a/api/server/server.go b/api/server/server.go index 61e8162659562..2485561b9dd36 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -611,7 +611,7 @@ func (s *Server) getContainersStats(eng *engine.Engine, version version.Version, return fmt.Errorf("Missing parameter") } - return s.daemon.ContainerStats(vars["name"], utils.NewWriteFlusher(w)) + return s.daemon.ContainerStats(vars["name"], boolValue(r, "stream"), utils.NewWriteFlusher(w)) } func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f3b83315873f4..f399de17744b6 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1003,7 +1003,7 @@ _docker_start() { _docker_stats() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--no-stream --help" -- "$cur" ) ) ;; *) __docker_containers_running diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index d3237588effe8..39ab9e33b4d55 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -16,7 +16,7 @@ function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand' for i in (commandline -opc) - if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs pause port ps pull push rename restart rm rmi run save search start stop tag top unpause version wait + if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs pause port ps pull push rename restart rm rmi run save search start stop tag top unpause version wait stats return 1 end end @@ -361,6 +361,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from start' -a '(__fish_prin # stats complete -c docker -f -n '__fish_docker_no_subcommand' -a stats -d "Display a live stream of one or more containers' resource usage statistics" complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l help -d 'Print usage' +complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -l no-stream -d 'Disable streaming stats and only pull the first result' complete -c docker -A -f -n '__fish_seen_subcommand_from stats' -a '(__fish_print_docker_containers running)' -d "Container" # stop diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 28398f7524105..3cff8fbbb6206 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -326,6 +326,7 @@ __docker_subcommand () { ;; (stats) _arguments \ + '--no-stream[Disable streaming stats and only pull the first result]' \ '*:containers:__docker_runningcontainers' ;; (rm) diff --git a/daemon/stats.go b/daemon/stats.go index a95168d128784..c7da913223d2d 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -10,7 +10,7 @@ import ( "github.com/docker/libcontainer/cgroups" ) -func (daemon *Daemon) ContainerStats(name string, out io.Writer) error { +func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) error { updates, err := daemon.SubscribeToContainerStats(name) if err != nil { return err @@ -27,6 +27,9 @@ func (daemon *Daemon) ContainerStats(name string, out io.Writer) error { daemon.UnsubscribeToContainerStats(name, updates) return err } + if !stream { + break + } } return nil } diff --git a/docs/man/docker-stats.1.md b/docs/man/docker-stats.1.md index f6fc3f7f23f1f..4b48588559566 100644 --- a/docs/man/docker-stats.1.md +++ b/docs/man/docker-stats.1.md @@ -17,6 +17,9 @@ Display a live stream of one or more containers' resource usage statistics **--help** Print usage statement +**--no-stream**="false" + Disable streaming stats and only pull the first result + # EXAMPLES Run **docker stats** with multiple containers. diff --git a/docs/sources/reference/api/docker_remote_api.md b/docs/sources/reference/api/docker_remote_api.md index d92084f29b476..676f210ebca5b 100644 --- a/docs/sources/reference/api/docker_remote_api.md +++ b/docs/sources/reference/api/docker_remote_api.md @@ -46,6 +46,11 @@ You can still call an old version of the API using ### What's new +`GET /containers/(id)/stats` + +**New!** +You can now supply a `stream` bool to get only one set of stats and +disconnect ## v1.18 diff --git a/docs/sources/reference/api/docker_remote_api_v1.19.md b/docs/sources/reference/api/docker_remote_api_v1.19.md index cede2e1073dd7..a214341d6898b 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.19.md +++ b/docs/sources/reference/api/docker_remote_api_v1.19.md @@ -644,6 +644,10 @@ This endpoint returns a live stream of a container's resource usage statistics. } } +Query Parameters: + +- **stream** – 1/True/true or 0/False/false, pull stats once then disconnect. Default true + Status Codes: - **200** – no error diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 26659c8ffa199..159fa80862300 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -2377,6 +2377,7 @@ more details on finding shared images from the command line. Display a live stream of one or more containers' resource usage statistics --help=false Print usage + --no-stream=false Disable streaming stats and only pull the first result Running `docker stats` on multiple containers diff --git a/integration-cli/docker_cli_stats_test.go b/integration-cli/docker_cli_stats_test.go new file mode 100644 index 0000000000000..7664de5977743 --- /dev/null +++ b/integration-cli/docker_cli_stats_test.go @@ -0,0 +1,36 @@ +package main + +import ( + "os/exec" + "strings" + "time" + + "github.com/go-check/check" +) + +func (s *DockerSuite) TestCliStatsNoStream(c *check.C) { + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "busybox", "top")) + if err != nil { + c.Fatalf("Error on container creation: %v, output: %s", err, out) + } + id := strings.TrimSpace(out) + if err := waitRun(id); err != nil { + c.Fatalf("error waiting for container to start: %v", err) + } + + statsCmd := exec.Command(dockerBinary, "stats", "--no-stream", id) + chErr := make(chan error) + go func() { + chErr <- statsCmd.Run() + }() + + select { + case err := <-chErr: + if err != nil { + c.Fatalf("Error running stats: %v", err) + } + case <-time.After(2 * time.Second): + statsCmd.Process.Kill() + c.Fatalf("stats did not return immediately when not streaming") + } +} From 49fd83a25e2e6604014de41d4f4099a7bc07a09b Mon Sep 17 00:00:00 2001 From: David Calavera Date: Fri, 24 Apr 2015 15:12:45 -0700 Subject: [PATCH 076/321] Use git url fragment to specify reference and dir context. Signed-off-by: David Calavera --- docs/sources/reference/commandline/cli.md | 25 ++++- integration-cli/docker_cli_build_test.go | 29 +++++ pkg/urlutil/git.go | 9 +- pkg/urlutil/git_test.go | 12 +++ utils/git.go | 60 +++++++++-- utils/git_test.go | 125 +++++++++++++++++++++- 6 files changed, 247 insertions(+), 13 deletions(-) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 26659c8ffa199..682946432377f 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -637,13 +637,36 @@ an [*ADD*](/reference/builder/#add) instruction to reference a file in the context. The `URL` parameter can specify the location of a Git repository; -the repository acts as the build context. The system recursively clones the repository +the repository acts as the build context. The system recursively clones the repository and its submodules using a `git clone --depth 1 --recursive` command. This command runs in a temporary directory on your local host. After the command succeeds, the directory is sent to the Docker daemon as the context. Local clones give you the ability to access private repositories using local user credentials, VPN's, and so forth. +Git URLs accept context configuration in their fragment section, separated by a colon `:`. +The first part represents the reference that Git will check out, this can be either +a branch, a tag, or a commit SHA. The second part represents a subdirectory +inside the repository that will be used as a build context. + +For example, run this command to use a directory called `docker` in the branch `container`: + + $ docker build https://github.com/docker/rootfs.git#container:docker + +The following table represents all the valid suffixes with their build contexts: + +Build Syntax Suffix | Commit Used | Build Context Used +--------------------|-------------|------------------- +`myrepo.git` | `refs/heads/master` | `/` +`myrepo.git#mytag` | `refs/tags/mytag` | `/` +`myrepo.git#mybranch` | `refs/heads/mybranch` | `/` +`myrepo.git#abcdef` | `sha1 = abcdef` | `/` +`myrepo.git#:myfolder` | `refs/heads/master` | `/myfolder` +`myrepo.git#master:myfolder` | `refs/heads/master` | `/myfolder` +`myrepo.git#mytag:myfolder` | `refs/tags/mytag` | `/myfolder` +`myrepo.git#mybranch:myfolder` | `refs/heads/mybranch` | `/myfolder` +`myrepo.git#abcdef:myfolder` | `sha1 = abcdef` | `/myfolder` + Instead of specifying a context, you can pass a single Dockerfile in the `URL` or pipe the file in via `STDIN`. To pipe a Dockerfile from `STDIN`: diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 72d15c177bbff..b06e29ae293b0 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -4221,6 +4221,35 @@ func (s *DockerSuite) TestBuildFromGIT(c *check.C) { } } +func (s *DockerSuite) TestBuildFromGITWithContext(c *check.C) { + name := "testbuildfromgit" + defer deleteImages(name) + git, err := fakeGIT("repo", map[string]string{ + "docker/Dockerfile": `FROM busybox + ADD first /first + RUN [ -f /first ] + MAINTAINER docker`, + "docker/first": "test git data", + }, true) + if err != nil { + c.Fatal(err) + } + defer git.Close() + + u := fmt.Sprintf("%s#master:docker", git.RepoURL) + _, err = buildImageFromPath(name, u, true) + if err != nil { + c.Fatal(err) + } + res, err := inspectField(name, "Author") + if err != nil { + c.Fatal(err) + } + if res != "docker" { + c.Fatalf("Maintainer should be docker, got %s", res) + } +} + func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *check.C) { name := "testbuildcmdcleanuponentrypoint" defer deleteImages(name) diff --git a/pkg/urlutil/git.go b/pkg/urlutil/git.go index ba88ddf6e64c5..dc4d6662e3f78 100644 --- a/pkg/urlutil/git.go +++ b/pkg/urlutil/git.go @@ -1,6 +1,9 @@ package urlutil -import "strings" +import ( + "regexp" + "strings" +) var ( validPrefixes = []string{ @@ -8,11 +11,13 @@ var ( "github.com/", "git@", } + + urlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$") ) // IsGitURL returns true if the provided str is a git repository URL. func IsGitURL(str string) bool { - if IsURL(str) && strings.HasSuffix(str, ".git") { + if IsURL(str) && urlPathWithFragmentSuffix.MatchString(str) { return true } for _, prefix := range validPrefixes { diff --git a/pkg/urlutil/git_test.go b/pkg/urlutil/git_test.go index 01dcea7da365d..bb89d8b5fd16b 100644 --- a/pkg/urlutil/git_test.go +++ b/pkg/urlutil/git_test.go @@ -9,10 +9,15 @@ var ( "git@bitbucket.org:atlassianlabs/atlassian-docker.git", "https://github.com/docker/docker.git", "http://github.com/docker/docker.git", + "http://github.com/docker/docker.git#branch", + "http://github.com/docker/docker.git#:dir", } incompleteGitUrls = []string{ "github.com/docker/docker", } + invalidGitUrls = []string{ + "http://github.com/docker/docker.git:#branch", + } ) func TestValidGitTransport(t *testing.T) { @@ -35,9 +40,16 @@ func TestIsGIT(t *testing.T) { t.Fatalf("%q should be detected as valid Git url", url) } } + for _, url := range incompleteGitUrls { if IsGitURL(url) == false { t.Fatalf("%q should be detected as valid Git url", url) } } + + for _, url := range invalidGitUrls { + if IsGitURL(url) == true { + t.Fatalf("%q should not be detected as valid Git prefix", url) + } + } } diff --git a/utils/git.go b/utils/git.go index 18e002d184210..ce8924d8afd76 100644 --- a/utils/git.go +++ b/utils/git.go @@ -4,7 +4,10 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" + "os" "os/exec" + "path/filepath" "strings" "github.com/docker/docker/pkg/urlutil" @@ -19,20 +22,26 @@ func GitClone(remoteURL string) (string, error) { return "", err } - clone := cloneArgs(remoteURL, root) + u, err := url.Parse(remoteURL) + if err != nil { + return "", err + } - if output, err := exec.Command("git", clone...).CombinedOutput(); err != nil { + fragment := u.Fragment + clone := cloneArgs(u, root) + + if output, err := git(clone...); err != nil { return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output) } - return root, nil + return checkoutGit(fragment, root) } -func cloneArgs(remoteURL, root string) []string { +func cloneArgs(remoteURL *url.URL, root string) []string { args := []string{"clone", "--recursive"} - shallow := true + shallow := len(remoteURL.Fragment) == 0 - if strings.HasPrefix(remoteURL, "http") { + if shallow && strings.HasPrefix(remoteURL.Scheme, "http") { res, err := http.Head(fmt.Sprintf("%s/info/refs?service=git-upload-pack", remoteURL)) if err != nil || res.Header.Get("Content-Type") != "application/x-git-upload-pack-advertisement" { shallow = false @@ -43,5 +52,42 @@ func cloneArgs(remoteURL, root string) []string { args = append(args, "--depth", "1") } - return append(args, remoteURL, root) + if remoteURL.Fragment != "" { + remoteURL.Fragment = "" + } + + return append(args, remoteURL.String(), root) +} + +func checkoutGit(fragment, root string) (string, error) { + refAndDir := strings.SplitN(fragment, ":", 2) + + if len(refAndDir[0]) != 0 { + if output, err := gitWithinDir(root, "checkout", refAndDir[0]); err != nil { + return "", fmt.Errorf("Error trying to use git: %s (%s)", err, output) + } + } + + if len(refAndDir) > 1 && len(refAndDir[1]) != 0 { + newCtx := filepath.Join(root, refAndDir[1]) + fi, err := os.Stat(newCtx) + if err != nil { + return "", err + } + if !fi.IsDir() { + return "", fmt.Errorf("Error setting git context, not a directory: %s", newCtx) + } + root = newCtx + } + + return root, nil +} + +func gitWithinDir(dir string, args ...string) ([]byte, error) { + a := []string{"--work-tree", dir, "--git-dir", filepath.Join(dir, ".git")} + return git(append(a, args...)...) +} + +func git(args ...string) ([]byte, error) { + return exec.Command("git", args...).CombinedOutput() } diff --git a/utils/git_test.go b/utils/git_test.go index a82841ae1167e..10b13e96272e3 100644 --- a/utils/git_test.go +++ b/utils/git_test.go @@ -2,9 +2,12 @@ package utils import ( "fmt" + "io/ioutil" "net/http" "net/http/httptest" "net/url" + "os" + "path/filepath" "reflect" "testing" ) @@ -22,7 +25,7 @@ func TestCloneArgsSmartHttp(t *testing.T) { w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", q)) }) - args := cloneArgs(gitURL, "/tmp") + args := cloneArgs(serverURL, "/tmp") exp := []string{"clone", "--recursive", "--depth", "1", gitURL, "/tmp"} if !reflect.DeepEqual(args, exp) { t.Fatalf("Expected %v, got %v", exp, args) @@ -41,16 +44,132 @@ func TestCloneArgsDumbHttp(t *testing.T) { w.Header().Set("Content-Type", "text/plain") }) - args := cloneArgs(gitURL, "/tmp") + args := cloneArgs(serverURL, "/tmp") exp := []string{"clone", "--recursive", gitURL, "/tmp"} if !reflect.DeepEqual(args, exp) { t.Fatalf("Expected %v, got %v", exp, args) } } + func TestCloneArgsGit(t *testing.T) { - args := cloneArgs("git://github.com/docker/docker", "/tmp") + u, _ := url.Parse("git://github.com/docker/docker") + args := cloneArgs(u, "/tmp") exp := []string{"clone", "--recursive", "--depth", "1", "git://github.com/docker/docker", "/tmp"} if !reflect.DeepEqual(args, exp) { t.Fatalf("Expected %v, got %v", exp, args) } } + +func TestCloneArgsStripFragment(t *testing.T) { + u, _ := url.Parse("git://github.com/docker/docker#test") + args := cloneArgs(u, "/tmp") + exp := []string{"clone", "--recursive", "git://github.com/docker/docker", "/tmp"} + if !reflect.DeepEqual(args, exp) { + t.Fatalf("Expected %v, got %v", exp, args) + } +} + +func TestCheckoutGit(t *testing.T) { + root, err := ioutil.TempDir("", "docker-build-git-checkout") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + gitDir := filepath.Join(root, "repo") + _, err = git("init", gitDir) + if err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "config", "user.email", "test@docker.com"); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "config", "user.name", "Docker test"); err != nil { + t.Fatal(err) + } + + if err = ioutil.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch"), 0644); err != nil { + t.Fatal(err) + } + + subDir := filepath.Join(gitDir, "subdir") + if err = os.Mkdir(subDir, 0755); err != nil { + t.Fatal(err) + } + + if err = ioutil.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 5000"), 0644); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "add", "-A"); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "commit", "-am", "First commit"); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "checkout", "-b", "test"); err != nil { + t.Fatal(err) + } + + if err = ioutil.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 3000"), 0644); err != nil { + t.Fatal(err) + } + + if err = ioutil.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM busybox\nEXPOSE 5000"), 0644); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "add", "-A"); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "commit", "-am", "Branch commit"); err != nil { + t.Fatal(err) + } + + if _, err = gitWithinDir(gitDir, "checkout", "master"); err != nil { + t.Fatal(err) + } + + cases := []struct { + frag string + exp string + fail bool + }{ + {"", "FROM scratch", false}, + {"master", "FROM scratch", false}, + {":subdir", "FROM scratch\nEXPOSE 5000", false}, + {":nosubdir", "", true}, // missing directory error + {":Dockerfile", "", true}, // not a directory error + {"master:nosubdir", "", true}, + {"master:subdir", "FROM scratch\nEXPOSE 5000", false}, + {"test", "FROM scratch\nEXPOSE 3000", false}, + {"test:", "FROM scratch\nEXPOSE 3000", false}, + {"test:subdir", "FROM busybox\nEXPOSE 5000", false}, + } + + for _, c := range cases { + r, err := checkoutGit(c.frag, gitDir) + + fail := err != nil + if fail != c.fail { + t.Fatalf("Expected %v failure, error was %v\n", c.fail, err) + } + if c.fail { + continue + } + + b, err := ioutil.ReadFile(filepath.Join(r, "Dockerfile")) + if err != nil { + t.Fatal(err) + } + + if string(b) != c.exp { + t.Fatalf("Expected %v, was %v\n", c.exp, string(b)) + } + } +} From 80a895142e7101b44ff71910bb2da994b1cc4f5f Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 4 May 2015 11:02:44 -0600 Subject: [PATCH 077/321] Update libcontainer and make it the source of truth on logrus version To help avoid version mismatches between libcontainer and Docker, this updates libcontainer to be the source of truth for which version of logrus the project is using. This should help avoid potential incompatibilities in the future, too. :+1: Signed-off-by: Andrew "Tianon" Page --- daemon/execdriver/native/init.go | 2 +- hack/vendor.sh | 6 +- .../github.com/Sirupsen/logrus/CHANGELOG.md | 4 + .../src/github.com/Sirupsen/logrus/README.md | 45 +- .../github.com/Sirupsen/logrus/formatter.go | 4 + .../logrus/formatters/logstash/logstash.go | 12 +- .../Sirupsen/logrus/json_formatter.go | 13 +- .../Sirupsen/logrus/text_formatter.go | 9 +- .../docker/libcontainer/apparmor/apparmor.go | 6 +- .../libcontainer/cgroups/fs/apply_raw.go | 49 +- .../docker/libcontainer/cgroups/fs/blkio.go | 26 ++ .../libcontainer/cgroups/fs/blkio_test.go | 124 +++++ .../docker/libcontainer/cgroups/fs/devices.go | 11 + .../libcontainer/cgroups/fs/devices_test.go | 38 +- .../docker/libcontainer/cgroups/fs/hugetlb.go | 29 ++ .../docker/libcontainer/cgroups/fs/memory.go | 1 + .../libcontainer/cgroups/fs/memory_test.go | 2 +- .../cgroups/fs/stats_util_test.go | 2 +- .../docker/libcontainer/cgroups/stats.go | 2 + .../cgroups/systemd/apply_nosystemd.go | 4 - .../cgroups/systemd/apply_systemd.go | 78 +++- .../docker/libcontainer/configs/cgroup.go | 17 + .../docker/libcontainer/configs/config.go | 7 + .../docker/libcontainer/configs/mount.go | 13 + .../docker/libcontainer/configs/namespaces.go | 31 +- .../configs/namespaces_syscall.go | 31 ++ .../configs/namespaces_syscall_unsupported.go | 15 + .../docker/libcontainer/configs/network.go | 4 +- .../docker/libcontainer/console_linux.go | 2 +- .../docker/libcontainer/container.go | 2 +- .../docker/libcontainer/container_linux.go | 22 +- .../docker/libcontainer/devices/devices.go | 2 +- .../github.com/docker/libcontainer/factory.go | 12 +- .../docker/libcontainer/factory_linux.go | 7 +- .../docker/libcontainer/init_linux.go | 21 +- .../libcontainer/integration/exec_test.go | 425 +++++++++++------- .../libcontainer/integration/execin_test.go | 192 ++++---- .../libcontainer/integration/init_test.go | 2 +- .../libcontainer/integration/utils_test.go | 11 + .../libcontainer/label/label_selinux.go | 14 +- .../libcontainer/label/label_selinux_test.go | 28 ++ .../docker/libcontainer/nsenter/README.md | 27 +- .../libcontainer/nsenter/nsenter_test.go | 2 +- .../docker/libcontainer/nsenter/nsexec.c | 23 +- .../docker/libcontainer/nsinit/README.md | 45 ++ .../docker/libcontainer/nsinit/config.go | 7 + .../docker/libcontainer/nsinit/exec.go | 1 + .../docker/libcontainer/nsinit/init.go | 2 +- .../docker/libcontainer/nsinit/oom.go | 3 +- .../docker/libcontainer/nsinit/pause.go | 3 +- .../docker/libcontainer/nsinit/utils.go | 12 +- .../github.com/docker/libcontainer/process.go | 7 +- .../docker/libcontainer/process_linux.go | 3 + .../docker/libcontainer/rootfs_linux.go | 80 +++- .../libcontainer/standard_init_linux.go | 7 + .../docker/libcontainer/system/setns_linux.go | 4 +- .../libcontainer/system/syscall_linux_64.go | 2 +- .../docker/libcontainer/update-vendor.sh | 2 +- 58 files changed, 1154 insertions(+), 401 deletions(-) create mode 100644 vendor/src/github.com/docker/libcontainer/cgroups/fs/hugetlb.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall.go create mode 100644 vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall_unsupported.go diff --git a/daemon/execdriver/native/init.go b/daemon/execdriver/native/init.go index f57d6cddec2d1..2a6cd26dab12a 100644 --- a/daemon/execdriver/native/init.go +++ b/daemon/execdriver/native/init.go @@ -32,7 +32,7 @@ func initializer() { if err != nil { fatal(err) } - if err := factory.StartInitialization(3); err != nil { + if err := factory.StartInitialization(); err != nil { fatal(err) } diff --git a/hack/vendor.sh b/hack/vendor.sh index de789d4db55e7..68d04f544aece 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -53,8 +53,6 @@ clone hg code.google.com/p/gosqlite 74691fb6f837 clone git github.com/docker/libtrust 230dfd18c232 -clone git github.com/Sirupsen/logrus v0.7.2 - clone git github.com/go-fsnotify/fsnotify v1.2.0 clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 @@ -69,8 +67,8 @@ mv tmp-digest src/github.com/docker/distribution/digest mkdir -p src/github.com/docker/distribution/registry mv tmp-api src/github.com/docker/distribution/registry/api -clone git github.com/docker/libcontainer bd8ec36106086f72b66e1be85a81202b93503e44 +clone git github.com/docker/libcontainer 6607689b1d06743003a45a722d9fe0bef36b274e # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) rm -rf src/github.com/docker/libcontainer/vendor -eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli' | grep -v 'github.com/Sirupsen/logrus')" +eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')" # we exclude "github.com/codegangsta/cli" here because it's only needed for "nsinit", which Docker doesn't include diff --git a/vendor/src/github.com/Sirupsen/logrus/CHANGELOG.md b/vendor/src/github.com/Sirupsen/logrus/CHANGELOG.md index 566a6fbd9d46e..eb72bff93b78b 100644 --- a/vendor/src/github.com/Sirupsen/logrus/CHANGELOG.md +++ b/vendor/src/github.com/Sirupsen/logrus/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.7.3 + +formatter/\*: allow configuration of timestamp layout + # 0.7.2 formatter/text: Add configuration option for time format (#158) diff --git a/vendor/src/github.com/Sirupsen/logrus/README.md b/vendor/src/github.com/Sirupsen/logrus/README.md index bf09541e83202..d55f90924792b 100644 --- a/vendor/src/github.com/Sirupsen/logrus/README.md +++ b/vendor/src/github.com/Sirupsen/logrus/README.md @@ -108,6 +108,16 @@ func main() { "omg": true, "number": 100, }).Fatal("The ice breaks!") + + // A common pattern is to re-use fields between logging statements by re-using + // the logrus.Entry returned from WithFields() + contextLogger := log.WithFields(log.Fields{ + "common": "this is a common field", + "other": "I also should be logged always", + }) + + contextLogger.Info("I'll be logged with common and other field") + contextLogger.Info("Me too") } ``` @@ -189,31 +199,18 @@ func init() { } ``` -* [`github.com/Sirupsen/logrus/hooks/airbrake`](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) - Send errors to an exception tracking service compatible with the Airbrake API. - Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. - -* [`github.com/Sirupsen/logrus/hooks/papertrail`](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) - Send errors to the Papertrail hosted logging service via UDP. - -* [`github.com/Sirupsen/logrus/hooks/syslog`](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) - Send errors to remote syslog server. - Uses standard library `log/syslog` behind the scenes. - -* [`github.com/Sirupsen/logrus/hooks/bugsnag`](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) - Send errors to the Bugsnag exception tracking service. - -* [`github.com/nubo/hiprus`](https://github.com/nubo/hiprus) - Send errors to a channel in hipchat. - -* [`github.com/sebest/logrusly`](https://github.com/sebest/logrusly) - Send logs to Loggly (https://www.loggly.com/) - -* [`github.com/johntdyer/slackrus`](https://github.com/johntdyer/slackrus) - Hook for Slack chat. -* [`github.com/wercker/journalhook`](https://github.com/wercker/journalhook). - Hook for logging to `systemd-journald`. +| Hook | Description | +| ----- | ----------- | +| [Airbrake](https://github.com/Sirupsen/logrus/blob/master/hooks/airbrake/airbrake.go) | Send errors to an exception tracking service compatible with the Airbrake API. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. | +| [Papertrail](https://github.com/Sirupsen/logrus/blob/master/hooks/papertrail/papertrail.go) | Send errors to the Papertrail hosted logging service via UDP. | +| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. | +| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. | +| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. | +| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) | +| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. | +| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` | +| [Graylog](https://github.com/gemnasium/logrus-hooks/tree/master/graylog) | Hook for logging to [Graylog](http://graylog2.org/) | #### Level logging diff --git a/vendor/src/github.com/Sirupsen/logrus/formatter.go b/vendor/src/github.com/Sirupsen/logrus/formatter.go index 038ce9fd2970a..104d689f187eb 100644 --- a/vendor/src/github.com/Sirupsen/logrus/formatter.go +++ b/vendor/src/github.com/Sirupsen/logrus/formatter.go @@ -1,5 +1,9 @@ package logrus +import "time" + +const DefaultTimestampFormat = time.RFC3339 + // The Formatter interface is used to implement a custom Formatter. It takes an // `Entry`. It exposes all the fields, including the default ones: // diff --git a/vendor/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go b/vendor/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go index 34b1ccbca60d1..8ea93ddf20a03 100644 --- a/vendor/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go +++ b/vendor/src/github.com/Sirupsen/logrus/formatters/logstash/logstash.go @@ -3,19 +3,27 @@ package logstash import ( "encoding/json" "fmt" + "github.com/Sirupsen/logrus" - "time" ) // Formatter generates json in logstash format. // Logstash site: http://logstash.net/ type LogstashFormatter struct { Type string // if not empty use for logstash type field. + + // TimestampFormat sets the format used for timestamps. + TimestampFormat string } func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) { entry.Data["@version"] = 1 - entry.Data["@timestamp"] = entry.Time.Format(time.RFC3339) + + if f.TimestampFormat == "" { + f.TimestampFormat = logrus.DefaultTimestampFormat + } + + entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat) // set message field v, ok := entry.Data["message"] diff --git a/vendor/src/github.com/Sirupsen/logrus/json_formatter.go b/vendor/src/github.com/Sirupsen/logrus/json_formatter.go index 5c4c44bbe557c..dcc4f1d9fd7e3 100644 --- a/vendor/src/github.com/Sirupsen/logrus/json_formatter.go +++ b/vendor/src/github.com/Sirupsen/logrus/json_formatter.go @@ -3,10 +3,12 @@ package logrus import ( "encoding/json" "fmt" - "time" ) -type JSONFormatter struct{} +type JSONFormatter struct { + // TimestampFormat sets the format used for marshaling timestamps. + TimestampFormat string +} func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { data := make(Fields, len(entry.Data)+3) @@ -21,7 +23,12 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } prefixFieldClashes(data) - data["time"] = entry.Time.Format(time.RFC3339) + + if f.TimestampFormat == "" { + f.TimestampFormat = DefaultTimestampFormat + } + + data["time"] = entry.Time.Format(f.TimestampFormat) data["msg"] = entry.Message data["level"] = entry.Level.String() diff --git a/vendor/src/github.com/Sirupsen/logrus/text_formatter.go b/vendor/src/github.com/Sirupsen/logrus/text_formatter.go index d3687ba25c725..612417ff9c846 100644 --- a/vendor/src/github.com/Sirupsen/logrus/text_formatter.go +++ b/vendor/src/github.com/Sirupsen/logrus/text_formatter.go @@ -18,9 +18,8 @@ const ( ) var ( - baseTimestamp time.Time - isTerminal bool - defaultTimestampFormat = time.RFC3339 + baseTimestamp time.Time + isTerminal bool ) func init() { @@ -47,7 +46,7 @@ type TextFormatter struct { // the time passed since beginning of execution. FullTimestamp bool - // Timestamp format to use for display, if a full timestamp is printed + // TimestampFormat to use for display when a full timestamp is printed TimestampFormat string // The fields are sorted by default for a consistent output. For applications @@ -73,7 +72,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { isColored := (f.ForceColors || isTerminal) && !f.DisableColors if f.TimestampFormat == "" { - f.TimestampFormat = defaultTimestampFormat + f.TimestampFormat = DefaultTimestampFormat } if isColored { f.printColored(b, entry, keys) diff --git a/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go b/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go index 3be3294d85dad..18cedf6a191a2 100644 --- a/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go +++ b/vendor/src/github.com/docker/libcontainer/apparmor/apparmor.go @@ -14,8 +14,10 @@ import ( func IsEnabled() bool { if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" { - buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled") - return err == nil && len(buf) > 1 && buf[0] == 'Y' + if _, err = os.Stat("/sbin/apparmor_parser"); err == nil { + buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled") + return err == nil && len(buf) > 1 && buf[0] == 'Y' + } } return false } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go index 0a2d76bcd4e8e..fa6478b5f32ba 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go @@ -1,6 +1,8 @@ package fs import ( + "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -19,6 +21,7 @@ var ( "cpuset": &CpusetGroup{}, "cpuacct": &CpuacctGroup{}, "blkio": &BlkioGroup{}, + "hugetlb": &HugetlbGroup{}, "perf_event": &PerfEventGroup{}, "freezer": &FreezerGroup{}, } @@ -75,10 +78,13 @@ type data struct { } func (m *Manager) Apply(pid int) error { + if m.Cgroups == nil { return nil } + var c = m.Cgroups + d, err := getCgroupData(m.Cgroups, pid) if err != nil { return err @@ -108,6 +114,12 @@ func (m *Manager) Apply(pid int) error { } m.Paths = paths + if paths["cpu"] != "" { + if err := CheckCpushares(paths["cpu"], c.CpuShares); err != nil { + return err + } + } + return nil } @@ -119,19 +131,6 @@ func (m *Manager) GetPaths() map[string]string { return m.Paths } -// Symmetrical public function to update device based cgroups. Also available -// in the systemd implementation. -func ApplyDevices(c *configs.Cgroup, pid int) error { - d, err := getCgroupData(c, pid) - if err != nil { - return err - } - - devices := subsystems["devices"] - - return devices.Apply(d) -} - func (m *Manager) GetStats() (*cgroups.Stats, error) { stats := cgroups.NewStats() for name, path := range m.Paths { @@ -280,3 +279,27 @@ func removePath(p string, err error) error { } return nil } + +func CheckCpushares(path string, c int64) error { + var cpuShares int64 + + fd, err := os.Open(filepath.Join(path, "cpu.shares")) + if err != nil { + return err + } + defer fd.Close() + + _, err = fmt.Fscanf(fd, "%d", &cpuShares) + if err != nil && err != io.EOF { + return err + } + if c != 0 { + if c > cpuShares { + return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares) + } else if c < cpuShares { + return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares) + } + } + + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go index 8e132643bb92e..06f0a3b2cd9a2 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio.go @@ -35,6 +35,32 @@ func (s *BlkioGroup) Set(path string, cgroup *configs.Cgroup) error { } } + if cgroup.BlkioWeightDevice != "" { + if err := writeFile(path, "blkio.weight_device", cgroup.BlkioWeightDevice); err != nil { + return err + } + } + if cgroup.BlkioThrottleReadBpsDevice != "" { + if err := writeFile(path, "blkio.throttle.read_bps_device", cgroup.BlkioThrottleReadBpsDevice); err != nil { + return err + } + } + if cgroup.BlkioThrottleWriteBpsDevice != "" { + if err := writeFile(path, "blkio.throttle.write_bps_device", cgroup.BlkioThrottleWriteBpsDevice); err != nil { + return err + } + } + if cgroup.BlkioThrottleReadIOpsDevice != "" { + if err := writeFile(path, "blkio.throttle.read_iops_device", cgroup.BlkioThrottleReadIOpsDevice); err != nil { + return err + } + } + if cgroup.BlkioThrottleWriteIOpsDevice != "" { + if err := writeFile(path, "blkio.throttle.write_iops_device", cgroup.BlkioThrottleWriteIOpsDevice); err != nil { + return err + } + } + return nil } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go index 9ef93fcff2c0a..9d0915da324e4 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/blkio_test.go @@ -67,6 +67,8 @@ Total 22061056` 252:0 Async 164 252:0 Total 164 Total 328` + throttleBefore = `8:0 1024` + throttleAfter = `8:0 2048` ) func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) { @@ -102,6 +104,35 @@ func TestBlkioSetWeight(t *testing.T) { } } +func TestBlkioSetWeightDevice(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + const ( + weightDeviceBefore = "8:0 400" + weightDeviceAfter = "8:0 500" + ) + + helper.writeFileContents(map[string]string{ + "blkio.weight_device": weightDeviceBefore, + }) + + helper.CgroupData.c.BlkioWeightDevice = weightDeviceAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "blkio.weight_device") + if err != nil { + t.Fatalf("Failed to parse blkio.weight_device - %s", err) + } + + if value != weightDeviceAfter { + t.Fatal("Got the wrong value, set blkio.weight_device failed.") + } +} + func TestBlkioStats(t *testing.T) { helper := NewCgroupTestUtil("blkio", t) defer helper.cleanup() @@ -442,3 +473,96 @@ func TestNonCFQBlkioStats(t *testing.T) { expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats) } + +func TestBlkioSetThrottleReadBpsDevice(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "blkio.throttle.read_bps_device": throttleBefore, + }) + + helper.CgroupData.c.BlkioThrottleReadBpsDevice = throttleAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_bps_device") + if err != nil { + t.Fatalf("Failed to parse blkio.throttle.read_bps_device - %s", err) + } + + if value != throttleAfter { + t.Fatal("Got the wrong value, set blkio.throttle.read_bps_device failed.") + } +} +func TestBlkioSetThrottleWriteBpsDevice(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "blkio.throttle.write_bps_device": throttleBefore, + }) + + helper.CgroupData.c.BlkioThrottleWriteBpsDevice = throttleAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_bps_device") + if err != nil { + t.Fatalf("Failed to parse blkio.throttle.write_bps_device - %s", err) + } + + if value != throttleAfter { + t.Fatal("Got the wrong value, set blkio.throttle.write_bps_device failed.") + } +} +func TestBlkioSetThrottleReadIOpsDevice(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "blkio.throttle.read_iops_device": throttleBefore, + }) + + helper.CgroupData.c.BlkioThrottleReadIOpsDevice = throttleAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.read_iops_device") + if err != nil { + t.Fatalf("Failed to parse blkio.throttle.read_iops_device - %s", err) + } + + if value != throttleAfter { + t.Fatal("Got the wrong value, set blkio.throttle.read_iops_device failed.") + } +} +func TestBlkioSetThrottleWriteIOpsDevice(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "blkio.throttle.write_iops_device": throttleBefore, + }) + + helper.CgroupData.c.BlkioThrottleWriteIOpsDevice = throttleAfter + blkio := &BlkioGroup{} + if err := blkio.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "blkio.throttle.write_iops_device") + if err != nil { + t.Fatalf("Failed to parse blkio.throttle.write_iops_device - %s", err) + } + + if value != throttleAfter { + t.Fatal("Got the wrong value, set blkio.throttle.write_iops_device failed.") + } +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go index 16e00b1c73b2b..be588d67a148c 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices.go @@ -32,6 +32,17 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error { return err } } + return nil + } + + if err := writeFile(path, "devices.allow", "a"); err != nil { + return err + } + + for _, dev := range cgroup.DeniedDevices { + if err := writeFile(path, "devices.deny", dev.CgroupString()); err != nil { + return err + } } return nil diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go index 18bb127462430..f950c1b9cf816 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/devices_test.go @@ -17,7 +17,18 @@ var ( FileMode: 0666, }, } - allowedList = "c 1:5 rwm" + allowedList = "c 1:5 rwm" + deniedDevices = []*configs.Device{ + { + Path: "/dev/null", + Type: 'c', + Major: 1, + Minor: 3, + Permissions: "rwm", + FileMode: 0666, + }, + } + deniedList = "c 1:3 rwm" ) func TestDevicesSetAllow(t *testing.T) { @@ -44,3 +55,28 @@ func TestDevicesSetAllow(t *testing.T) { t.Fatal("Got the wrong value, set devices.allow failed.") } } + +func TestDevicesSetDeny(t *testing.T) { + helper := NewCgroupTestUtil("devices", t) + defer helper.cleanup() + + helper.writeFileContents(map[string]string{ + "devices.allow": "a", + }) + + helper.CgroupData.c.AllowAllDevices = true + helper.CgroupData.c.DeniedDevices = deniedDevices + devices := &DevicesGroup{} + if err := devices.Set(helper.CgroupPath, helper.CgroupData.c); err != nil { + t.Fatal(err) + } + + value, err := getCgroupParamString(helper.CgroupPath, "devices.deny") + if err != nil { + t.Fatalf("Failed to parse devices.deny - %s", err) + } + + if value != deniedList { + t.Fatal("Got the wrong value, set devices.deny failed.") + } +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/hugetlb.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/hugetlb.go new file mode 100644 index 0000000000000..8defdd1b914aa --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/hugetlb.go @@ -0,0 +1,29 @@ +package fs + +import ( + "github.com/docker/libcontainer/cgroups" + "github.com/docker/libcontainer/configs" +) + +type HugetlbGroup struct { +} + +func (s *HugetlbGroup) Apply(d *data) error { + // we just want to join this group even though we don't set anything + if _, err := d.join("hugetlb"); err != nil && !cgroups.IsNotFound(err) { + return err + } + return nil +} + +func (s *HugetlbGroup) Set(path string, cgroup *configs.Cgroup) error { + return nil +} + +func (s *HugetlbGroup) Remove(d *data) error { + return removePath(d.path("hugetlb")) +} + +func (s *HugetlbGroup) GetStats(path string, stats *cgroups.Stats) error { + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go index b99f81687a6a8..d5dbaf6570dc7 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory.go @@ -95,6 +95,7 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error { return fmt.Errorf("failed to parse memory.usage_in_bytes - %v", err) } stats.MemoryStats.Usage = value + stats.MemoryStats.Cache = stats.MemoryStats.Stats["cache"] value, err = getCgroupParamUint(path, "memory.max_usage_in_bytes") if err != nil { return fmt.Errorf("failed to parse memory.max_usage_in_bytes - %v", err) diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go index 1e939c4e888be..60edc67a524b7 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/memory_test.go @@ -128,7 +128,7 @@ func TestMemoryStats(t *testing.T) { if err != nil { t.Fatal(err) } - expectedStats := cgroups.MemoryStats{Usage: 2048, MaxUsage: 4096, Failcnt: 100, Stats: map[string]uint64{"cache": 512, "rss": 1024}} + expectedStats := cgroups.MemoryStats{Usage: 2048, Cache: 512, MaxUsage: 4096, Failcnt: 100, Stats: map[string]uint64{"cache": 512, "rss": 1024}} expectMemoryStatEquals(t, expectedStats, actualStats.MemoryStats) } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go index c55ba938cbc33..b94f60f99eb8b 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/stats_util_test.go @@ -2,9 +2,9 @@ package fs import ( "fmt" - "log" "testing" + log "github.com/Sirupsen/logrus" "github.com/docker/libcontainer/cgroups" ) diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/stats.go b/vendor/src/github.com/docker/libcontainer/cgroups/stats.go index dc5dbb3c21b4a..25c8f199cc31a 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/stats.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/stats.go @@ -33,6 +33,8 @@ type CpuStats struct { type MemoryStats struct { // current res_counter usage for memory Usage uint64 `json:"usage,omitempty"` + // memory used for cache + Cache uint64 `json:"cache,omitempty"` // maximum usage ever recorded. MaxUsage uint64 `json:"max_usage,omitempty"` // TODO(vishh): Export these as stronger types. diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go index 95ed4ea7eb280..9b605b3c05dfa 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_nosystemd.go @@ -46,10 +46,6 @@ func (m *Manager) Freeze(state configs.FreezerState) error { return fmt.Errorf("Systemd not supported") } -func ApplyDevices(c *configs.Cgroup, pid int) error { - return fmt.Errorf("Systemd not supported") -} - func Freeze(c *configs.Cgroup, state configs.FreezerState) error { return fmt.Errorf("Systemd not supported") } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go index 3609bccae62f8..2ba10cbb3430d 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go @@ -38,6 +38,7 @@ var subsystems = map[string]subsystem{ "cpuset": &fs.CpusetGroup{}, "cpuacct": &fs.CpuacctGroup{}, "blkio": &fs.BlkioGroup{}, + "hugetlb": &fs.HugetlbGroup{}, "perf_event": &fs.PerfEventGroup{}, "freezer": &fs.FreezerGroup{}, } @@ -216,6 +217,13 @@ func (m *Manager) Apply(pid int) error { return err } + // FIXME: Systemd does have `BlockIODeviceWeight` property, but we got problem + // using that (at least on systemd 208, see https://github.com/docker/libcontainer/pull/354), + // so use fs work around for now. + if err := joinBlkio(c, pid); err != nil { + return err + } + paths := make(map[string]string) for sysname := range subsystems { subsystemPath, err := getSubsystemPath(m.Cgroups, sysname) @@ -228,9 +236,14 @@ func (m *Manager) Apply(pid int) error { } paths[sysname] = subsystemPath } - m.Paths = paths + if paths["cpu"] != "" { + if err := fs.CheckCpushares(paths["cpu"], c.CpuShares); err != nil { + return err + } + } + return nil } @@ -350,7 +363,17 @@ func (m *Manager) GetStats() (*cgroups.Stats, error) { } func (m *Manager) Set(container *configs.Config) error { - panic("not implemented") + for name, path := range m.Paths { + sys, ok := subsystems[name] + if !ok || !cgroups.PathExists(path) { + continue + } + if err := sys.Set(path, container.Cgroups); err != nil { + return err + } + } + + return nil } func getUnitName(c *configs.Cgroup) string { @@ -362,7 +385,7 @@ func getUnitName(c *configs.Cgroup) string { // * Support for wildcards to allow /dev/pts support // // The second is available in more recent systemd as "char-pts", but not in e.g. v208 which is -// in wide use. When both these are availalable we will be able to switch, but need to keep the old +// in wide use. When both these are available we will be able to switch, but need to keep the old // implementation for backwards compat. // // Note: we can't use systemd to set up the initial limits, and then change the cgroup @@ -375,17 +398,7 @@ func joinDevices(c *configs.Cgroup, pid int) error { } devices := subsystems["devices"] - if err := devices.Set(path, c); err != nil { - return err - } - - return nil -} - -// Symmetrical public function to update device based cgroups. Also available -// in the fs implementation. -func ApplyDevices(c *configs.Cgroup, pid int) error { - return joinDevices(c, pid) + return devices.Set(path, c) } func joinMemory(c *configs.Cgroup, pid int) error { @@ -417,3 +430,40 @@ func joinCpuset(c *configs.Cgroup, pid int) error { return s.ApplyDir(path, c, pid) } + +// `BlockIODeviceWeight` property of systemd does not work properly, and systemd +// expects device path instead of major minor numbers, which is also confusing +// for users. So we use fs work around for now. +func joinBlkio(c *configs.Cgroup, pid int) error { + path, err := getSubsystemPath(c, "blkio") + if err != nil { + return err + } + if c.BlkioWeightDevice != "" { + if err := writeFile(path, "blkio.weight_device", c.BlkioWeightDevice); err != nil { + return err + } + } + if c.BlkioThrottleReadBpsDevice != "" { + if err := writeFile(path, "blkio.throttle.read_bps_device", c.BlkioThrottleReadBpsDevice); err != nil { + return err + } + } + if c.BlkioThrottleWriteBpsDevice != "" { + if err := writeFile(path, "blkio.throttle.write_bps_device", c.BlkioThrottleWriteBpsDevice); err != nil { + return err + } + } + if c.BlkioThrottleReadIOpsDevice != "" { + if err := writeFile(path, "blkio.throttle.read_iops_device", c.BlkioThrottleReadIOpsDevice); err != nil { + return err + } + } + if c.BlkioThrottleWriteIOpsDevice != "" { + if err := writeFile(path, "blkio.throttle.write_iops_device", c.BlkioThrottleWriteIOpsDevice); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/cgroup.go b/vendor/src/github.com/docker/libcontainer/configs/cgroup.go index 8bf174c195f2f..8a161fcff66f9 100644 --- a/vendor/src/github.com/docker/libcontainer/configs/cgroup.go +++ b/vendor/src/github.com/docker/libcontainer/configs/cgroup.go @@ -19,6 +19,8 @@ type Cgroup struct { AllowedDevices []*Device `json:"allowed_devices"` + DeniedDevices []*Device `json:"denied_devices"` + // Memory limit (in bytes) Memory int64 `json:"memory"` @@ -43,9 +45,24 @@ type Cgroup struct { // MEM to use CpusetMems string `json:"cpuset_mems"` + // IO read rate limit per cgroup per device, bytes per second. + BlkioThrottleReadBpsDevice string `json:"blkio_throttle_read_bps_device"` + + // IO write rate limit per cgroup per divice, bytes per second. + BlkioThrottleWriteBpsDevice string `json:"blkio_throttle_write_bps_device"` + + // IO read rate limit per cgroup per device, IO per second. + BlkioThrottleReadIOpsDevice string `json:"blkio_throttle_read_iops_device"` + + // IO write rate limit per cgroup per device, IO per second. + BlkioThrottleWriteIOpsDevice string `json:"blkio_throttle_write_iops_device"` + // Specifies per cgroup weight, range is from 10 to 1000. BlkioWeight int64 `json:"blkio_weight"` + // Weight per cgroup per device, can override BlkioWeight. + BlkioWeightDevice string `json:"blkio_weight_device"` + // set the freeze value for the process Freezer FreezerState `json:"freezer"` diff --git a/vendor/src/github.com/docker/libcontainer/configs/config.go b/vendor/src/github.com/docker/libcontainer/configs/config.go index b07f252b5e620..2c311a0cdf1ff 100644 --- a/vendor/src/github.com/docker/libcontainer/configs/config.go +++ b/vendor/src/github.com/docker/libcontainer/configs/config.go @@ -37,6 +37,9 @@ type Config struct { // bind mounts are writtable. Readonlyfs bool `json:"readonlyfs"` + // Privatefs will mount the container's rootfs as private where mount points from the parent will not propogate + Privatefs bool `json:"privatefs"` + // Mounts specify additional source and destination paths that will be mounted inside the container's // rootfs and mount namespace if specified Mounts []*Mount `json:"mounts"` @@ -96,6 +99,10 @@ type Config struct { // ReadonlyPaths specifies paths within the container's rootfs to remount as read-only // so that these files prevent any writes. ReadonlyPaths []string `json:"readonly_paths"` + + // SystemProperties is a map of properties and their values. It is the equivalent of using + // sysctl -w my.property.name value in Linux. + SystemProperties map[string]string `json:"system_properties"` } // Gets the root uid for the process on host which could be non-zero diff --git a/vendor/src/github.com/docker/libcontainer/configs/mount.go b/vendor/src/github.com/docker/libcontainer/configs/mount.go index 7b3dea3312a54..5a69f815e4e3a 100644 --- a/vendor/src/github.com/docker/libcontainer/configs/mount.go +++ b/vendor/src/github.com/docker/libcontainer/configs/mount.go @@ -18,4 +18,17 @@ type Mount struct { // Relabel source if set, "z" indicates shared, "Z" indicates unshared. Relabel string `json:"relabel"` + + // Optional Command to be run before Source is mounted. + PremountCmds []Command `json:"premount_cmds"` + + // Optional Command to be run after Source is mounted. + PostmountCmds []Command `json:"postmount_cmds"` +} + +type Command struct { + Path string `json:"path"` + Args []string `json:"args"` + Env []string `json:"env"` + Dir string `json:"dir"` } diff --git a/vendor/src/github.com/docker/libcontainer/configs/namespaces.go b/vendor/src/github.com/docker/libcontainer/configs/namespaces.go index ac6a7fa2cdb6f..2c2a9fd20a9d2 100644 --- a/vendor/src/github.com/docker/libcontainer/configs/namespaces.go +++ b/vendor/src/github.com/docker/libcontainer/configs/namespaces.go @@ -1,9 +1,6 @@ package configs -import ( - "fmt" - "syscall" -) +import "fmt" type NamespaceType string @@ -34,10 +31,6 @@ type Namespace struct { Path string `json:"path"` } -func (n *Namespace) Syscall() int { - return namespaceInfo[n.Type] -} - func (n *Namespace) GetPath(pid int) string { if n.Path != "" { return n.Path @@ -96,25 +89,3 @@ func (n *Namespaces) index(t NamespaceType) int { func (n *Namespaces) Contains(t NamespaceType) bool { return n.index(t) != -1 } - -var namespaceInfo = map[NamespaceType]int{ - NEWNET: syscall.CLONE_NEWNET, - NEWNS: syscall.CLONE_NEWNS, - NEWUSER: syscall.CLONE_NEWUSER, - NEWIPC: syscall.CLONE_NEWIPC, - NEWUTS: syscall.CLONE_NEWUTS, - NEWPID: syscall.CLONE_NEWPID, -} - -// CloneFlags parses the container's Namespaces options to set the correct -// flags on clone, unshare. This functions returns flags only for new namespaces. -func (n *Namespaces) CloneFlags() uintptr { - var flag int - for _, v := range *n { - if v.Path != "" { - continue - } - flag |= namespaceInfo[v.Type] - } - return uintptr(flag) -} diff --git a/vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall.go b/vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall.go new file mode 100644 index 0000000000000..c962999efd4bc --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall.go @@ -0,0 +1,31 @@ +// +build linux + +package configs + +import "syscall" + +func (n *Namespace) Syscall() int { + return namespaceInfo[n.Type] +} + +var namespaceInfo = map[NamespaceType]int{ + NEWNET: syscall.CLONE_NEWNET, + NEWNS: syscall.CLONE_NEWNS, + NEWUSER: syscall.CLONE_NEWUSER, + NEWIPC: syscall.CLONE_NEWIPC, + NEWUTS: syscall.CLONE_NEWUTS, + NEWPID: syscall.CLONE_NEWPID, +} + +// CloneFlags parses the container's Namespaces options to set the correct +// flags on clone, unshare. This functions returns flags only for new namespaces. +func (n *Namespaces) CloneFlags() uintptr { + var flag int + for _, v := range *n { + if v.Path != "" { + continue + } + flag |= namespaceInfo[v.Type] + } + return uintptr(flag) +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall_unsupported.go b/vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall_unsupported.go new file mode 100644 index 0000000000000..1bd26bd6e63eb --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/configs/namespaces_syscall_unsupported.go @@ -0,0 +1,15 @@ +// +build !linux + +package configs + +func (n *Namespace) Syscall() int { + panic("No namespace syscall support") + return 0 +} + +// CloneFlags parses the container's Namespaces options to set the correct +// flags on clone, unshare. This functions returns flags only for new namespaces. +func (n *Namespaces) CloneFlags() uintptr { + panic("No namespace syscall support") + return uintptr(0) +} diff --git a/vendor/src/github.com/docker/libcontainer/configs/network.go b/vendor/src/github.com/docker/libcontainer/configs/network.go index 9d5ed7a65fa09..ccdb228e14c85 100644 --- a/vendor/src/github.com/docker/libcontainer/configs/network.go +++ b/vendor/src/github.com/docker/libcontainer/configs/network.go @@ -2,7 +2,7 @@ package configs // Network defines configuration for a container's networking stack // -// The network configuration can be omited from a container causing the +// The network configuration can be omitted from a container causing the // container to be setup with the host's networking stack type Network struct { // Type sets the networks type, commonly veth and loopback @@ -53,7 +53,7 @@ type Network struct { // Routes can be specified to create entries in the route table as the container is started // // All of destination, source, and gateway should be either IPv4 or IPv6. -// One of the three options must be present, and ommitted entries will use their +// One of the three options must be present, and omitted entries will use their // IP family default for the route table. For IPv4 for example, setting the // gateway to 1.2.3.4 and the interface to eth0 will set up a standard // destination of 0.0.0.0(or *) when viewed in the route table. diff --git a/vendor/src/github.com/docker/libcontainer/console_linux.go b/vendor/src/github.com/docker/libcontainer/console_linux.go index afdc2976c4454..a3a0551cf688c 100644 --- a/vendor/src/github.com/docker/libcontainer/console_linux.go +++ b/vendor/src/github.com/docker/libcontainer/console_linux.go @@ -38,7 +38,7 @@ func newConsole(uid, gid int) (Console, error) { }, nil } -// newConsoleFromPath is an internal fucntion returning an initialzied console for use inside +// newConsoleFromPath is an internal function returning an initialized console for use inside // a container's MNT namespace. func newConsoleFromPath(slavePath string) *linuxConsole { return &linuxConsole{ diff --git a/vendor/src/github.com/docker/libcontainer/container.go b/vendor/src/github.com/docker/libcontainer/container.go index 35bdfd781f3f1..a38df8269d820 100644 --- a/vendor/src/github.com/docker/libcontainer/container.go +++ b/vendor/src/github.com/docker/libcontainer/container.go @@ -67,7 +67,7 @@ type Container interface { // State returns the current container's state information. // // errors: - // Systemerror - System erroor. + // Systemerror - System error. State() (*State, error) // Returns the current config of the container. diff --git a/vendor/src/github.com/docker/libcontainer/container_linux.go b/vendor/src/github.com/docker/libcontainer/container_linux.go index d52610f073c37..1ffd7d9cbe42e 100644 --- a/vendor/src/github.com/docker/libcontainer/container_linux.go +++ b/vendor/src/github.com/docker/libcontainer/container_linux.go @@ -16,6 +16,8 @@ import ( "github.com/docker/libcontainer/configs" ) +const stdioFdCount = 3 + type linuxContainer struct { id string root string @@ -139,7 +141,8 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec. if cmd.SysProcAttr == nil { cmd.SysProcAttr = &syscall.SysProcAttr{} } - cmd.ExtraFiles = []*os.File{childPipe} + cmd.ExtraFiles = append(p.ExtraFiles, childPipe) + cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1)) // NOTE: when running a container with no PID namespace and the parent process spawning the container is // PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason // even with the parent still running. @@ -178,11 +181,9 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()), "_LIBCONTAINER_INITTYPE=setns", ) - if p.consolePath != "" { cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath) } - // TODO: set on container for process management return &setnsProcess{ cmd: cmd, @@ -195,13 +196,14 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, func (c *linuxContainer) newInitConfig(process *Process) *initConfig { return &initConfig{ - Config: c.config, - Args: process.Args, - Env: process.Env, - User: process.User, - Cwd: process.Cwd, - Console: process.consolePath, - Capabilities: process.Capabilities, + Config: c.config, + Args: process.Args, + Env: process.Env, + User: process.User, + Cwd: process.Cwd, + Console: process.consolePath, + Capabilities: process.Capabilities, + PassedFilesCount: len(process.ExtraFiles), } } diff --git a/vendor/src/github.com/docker/libcontainer/devices/devices.go b/vendor/src/github.com/docker/libcontainer/devices/devices.go index 537f71aff1f05..7a11eaf11bb01 100644 --- a/vendor/src/github.com/docker/libcontainer/devices/devices.go +++ b/vendor/src/github.com/docker/libcontainer/devices/devices.go @@ -21,7 +21,7 @@ var ( ioutilReadDir = ioutil.ReadDir ) -// Given the path to a device and it's cgroup_permissions(which cannot be easilly queried) look up the information about a linux device and return that information as a Device struct. +// Given the path to a device and it's cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct. func DeviceFromPath(path, permissions string) (*configs.Device, error) { fileInfo, err := osLstat(path) if err != nil { diff --git a/vendor/src/github.com/docker/libcontainer/factory.go b/vendor/src/github.com/docker/libcontainer/factory.go index 0c9fa63a326f1..2b3ff85d8f773 100644 --- a/vendor/src/github.com/docker/libcontainer/factory.go +++ b/vendor/src/github.com/docker/libcontainer/factory.go @@ -32,15 +32,13 @@ type Factory interface { // System error Load(id string) (Container, error) - // StartInitialization is an internal API to libcontainer used during the rexec of the - // container. pipefd is the fd to the child end of the pipe used to syncronize the - // parent and child process providing state and configuration to the child process and - // returning any errors during the init of the container + // StartInitialization is an internal API to libcontainer used during the reexec of the + // container. // // Errors: - // pipe connection error - // system error - StartInitialization(pipefd uintptr) error + // Pipe connection error + // System error + StartInitialization() error // Type returns info string about factory type (e.g. lxc, libcontainer...) Type() string diff --git a/vendor/src/github.com/docker/libcontainer/factory_linux.go b/vendor/src/github.com/docker/libcontainer/factory_linux.go index a2d3bec780f07..3cf1c3d25f0ae 100644 --- a/vendor/src/github.com/docker/libcontainer/factory_linux.go +++ b/vendor/src/github.com/docker/libcontainer/factory_linux.go @@ -10,6 +10,7 @@ import ( "os/exec" "path/filepath" "regexp" + "strconv" "syscall" "github.com/docker/docker/pkg/mount" @@ -194,7 +195,11 @@ func (l *LinuxFactory) Type() string { // StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state // This is a low level implementation detail of the reexec and should not be consumed externally -func (l *LinuxFactory) StartInitialization(pipefd uintptr) (err error) { +func (l *LinuxFactory) StartInitialization() (err error) { + pipefd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_INITPIPE")) + if err != nil { + return err + } var ( pipe = os.NewFile(uintptr(pipefd), "pipe") it = initType(os.Getenv("_LIBCONTAINER_INITTYPE")) diff --git a/vendor/src/github.com/docker/libcontainer/init_linux.go b/vendor/src/github.com/docker/libcontainer/init_linux.go index 1786b1ed7a5cc..4bbb713d0625c 100644 --- a/vendor/src/github.com/docker/libcontainer/init_linux.go +++ b/vendor/src/github.com/docker/libcontainer/init_linux.go @@ -40,14 +40,15 @@ type network struct { // initConfig is used for transferring parameters from Exec() to Init() type initConfig struct { - Args []string `json:"args"` - Env []string `json:"env"` - Cwd string `json:"cwd"` - Capabilities []string `json:"capabilities"` - User string `json:"user"` - Config *configs.Config `json:"config"` - Console string `json:"console"` - Networks []*network `json:"network"` + Args []string `json:"args"` + Env []string `json:"env"` + Cwd string `json:"cwd"` + Capabilities []string `json:"capabilities"` + User string `json:"user"` + Config *configs.Config `json:"config"` + Console string `json:"console"` + Networks []*network `json:"network"` + PassedFilesCount int `json:"passed_files_count"` } type initer interface { @@ -95,10 +96,10 @@ func populateProcessEnvironment(env []string) error { // and working dir, and closes any leaked file descriptors // before executing the command inside the namespace func finalizeNamespace(config *initConfig) error { - // Ensure that all non-standard fds we may have accidentally + // Ensure that all unwanted fds we may have accidentally // inherited are marked close-on-exec so they stay out of the // container - if err := utils.CloseExecFrom(3); err != nil { + if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil { return err } diff --git a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go index 12457ba1a2135..5ee9b9e9e321f 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go @@ -4,8 +4,10 @@ import ( "bytes" "io/ioutil" "os" + "path/filepath" "strconv" "strings" + "syscall" "testing" "github.com/docker/libcontainer" @@ -29,9 +31,7 @@ func testExecPS(t *testing.T, userns bool) { return } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) if userns { @@ -64,21 +64,15 @@ func TestIPCPrivate(t *testing.T) { } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) l, err := os.Readlink("/proc/1/ns/ipc") - if err != nil { - t.Fatal(err) - } + ok(t, err) config := newTemplateConfig(rootfs) buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") - if err != nil { - t.Fatal(err) - } + ok(t, err) if exitCode != 0 { t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) @@ -95,22 +89,16 @@ func TestIPCHost(t *testing.T) { } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) l, err := os.Readlink("/proc/1/ns/ipc") - if err != nil { - t.Fatal(err) - } + ok(t, err) config := newTemplateConfig(rootfs) config.Namespaces.Remove(configs.NEWIPC) buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") - if err != nil { - t.Fatal(err) - } + ok(t, err) if exitCode != 0 { t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) @@ -127,23 +115,17 @@ func TestIPCJoinPath(t *testing.T) { } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) l, err := os.Readlink("/proc/1/ns/ipc") - if err != nil { - t.Fatal(err) - } + ok(t, err) config := newTemplateConfig(rootfs) config.Namespaces.Add(configs.NEWIPC, "/proc/1/ns/ipc") buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") - if err != nil { - t.Fatal(err) - } + ok(t, err) if exitCode != 0 { t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) @@ -160,9 +142,7 @@ func TestIPCBadPath(t *testing.T) { } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) @@ -180,16 +160,12 @@ func TestRlimit(t *testing.T) { } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) out, _, err := runContainer(config, "", "/bin/sh", "-c", "ulimit -n") - if err != nil { - t.Fatal(err) - } + ok(t, err) if limit := strings.TrimSpace(out.Stdout.String()); limit != "1025" { t.Fatalf("expected rlimit to be 1025, got %s", limit) } @@ -208,9 +184,7 @@ func newTestRoot() (string, error) { func waitProcess(p *libcontainer.Process, t *testing.T) { status, err := p.Wait() - if err != nil { - t.Fatal(err) - } + ok(t, err) if !status.Success() { t.Fatal(status) } @@ -221,35 +195,25 @@ func TestEnter(t *testing.T) { return } root, err := newTestRoot() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer os.RemoveAll(root) rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs) - if err != nil { - t.Fatal(err) - } + ok(t, err) container, err := factory.Create("test", config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() // Execute a first process in the container stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) var stdout, stdout2 bytes.Buffer @@ -262,19 +226,13 @@ func TestEnter(t *testing.T) { err = container.Start(&pconfig) stdinR.Close() defer stdinW.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) pid, err := pconfig.Pid() - if err != nil { - t.Fatal(err) - } + ok(t, err) // Execute another process in the container stdinR2, stdinW2, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) pconfig2 := libcontainer.Process{ Env: standardEnvironment, } @@ -285,19 +243,13 @@ func TestEnter(t *testing.T) { err = container.Start(&pconfig2) stdinR2.Close() defer stdinW2.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) pid2, err := pconfig2.Pid() - if err != nil { - t.Fatal(err) - } + ok(t, err) processes, err := container.Processes() - if err != nil { - t.Fatal(err) - } + ok(t, err) n := 0 for i := range processes { @@ -318,14 +270,10 @@ func TestEnter(t *testing.T) { // Check that both processes live in the same pidns pidns := string(stdout.Bytes()) - if err != nil { - t.Fatal(err) - } + ok(t, err) pidns2 := string(stdout2.Bytes()) - if err != nil { - t.Fatal(err) - } + ok(t, err) if pidns != pidns2 { t.Fatal("The second process isn't in the required pid namespace", pidns, pidns2) @@ -337,28 +285,20 @@ func TestProcessEnv(t *testing.T) { return } root, err := newTestRoot() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer os.RemoveAll(root) rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs) - if err != nil { - t.Fatal(err) - } + ok(t, err) container, err := factory.Create("test", config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() var stdout bytes.Buffer @@ -374,17 +314,12 @@ func TestProcessEnv(t *testing.T) { Stdout: &stdout, } err = container.Start(&pconfig) - if err != nil { - t.Fatal(err) - } + ok(t, err) // Wait for process waitProcess(&pconfig, t) outputEnv := string(stdout.Bytes()) - if err != nil { - t.Fatal(err) - } // Check that the environment has the key/value pair we added if !strings.Contains(outputEnv, "FOO=BAR") { @@ -402,28 +337,20 @@ func TestProcessCaps(t *testing.T) { return } root, err := newTestRoot() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer os.RemoveAll(root) rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) factory, err := libcontainer.New(root, libcontainer.Cgroupfs) - if err != nil { - t.Fatal(err) - } + ok(t, err) container, err := factory.Create("test", config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() processCaps := append(config.Capabilities, "NET_ADMIN") @@ -437,17 +364,12 @@ func TestProcessCaps(t *testing.T) { Stdout: &stdout, } err = container.Start(&pconfig) - if err != nil { - t.Fatal(err) - } + ok(t, err) // Wait for process waitProcess(&pconfig, t) outputStatus := string(stdout.Bytes()) - if err != nil { - t.Fatal(err) - } lines := strings.Split(outputStatus, "\n") @@ -497,37 +419,28 @@ func testFreeze(t *testing.T, systemd bool) { return } root, err := newTestRoot() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer os.RemoveAll(root) rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) + cgm := libcontainer.Cgroupfs if systemd { - config.Cgroups.Slice = "system.slice" + cgm = libcontainer.SystemdCgroups } - factory, err := libcontainer.New(root, libcontainer.Cgroupfs) - if err != nil { - t.Fatal(err) - } + factory, err := libcontainer.New(root, cgm) + ok(t, err) container, err := factory.Create("test", config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) pconfig := libcontainer.Process{ Args: []string{"cat"}, @@ -537,44 +450,64 @@ func testFreeze(t *testing.T, systemd bool) { err = container.Start(&pconfig) stdinR.Close() defer stdinW.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) pid, err := pconfig.Pid() - if err != nil { - t.Fatal(err) - } + ok(t, err) process, err := os.FindProcess(pid) - if err != nil { - t.Fatal(err) - } + ok(t, err) - if err := container.Pause(); err != nil { - t.Fatal(err) - } + err = container.Pause() + ok(t, err) state, err := container.Status() - if err != nil { - t.Fatal(err) - } - if err := container.Resume(); err != nil { - t.Fatal(err) - } + ok(t, err) + err = container.Resume() + ok(t, err) if state != libcontainer.Paused { t.Fatal("Unexpected state: ", state) } stdinW.Close() s, err := process.Wait() - if err != nil { - t.Fatal(err) - } + ok(t, err) + if !s.Success() { t.Fatal(s.String()) } } +func TestCpuShares(t *testing.T) { + testCpuShares(t, false) +} + +func TestSystemdCpuShares(t *testing.T) { + if !systemd.UseSystemd() { + t.Skip("Systemd is unsupported") + } + testCpuShares(t, true) +} + +func testCpuShares(t *testing.T, systemd bool) { + if testing.Short() { + return + } + rootfs, err := newRootfs() + ok(t, err) + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + if systemd { + config.Cgroups.Slice = "system.slice" + } + config.Cgroups.CpuShares = 1 + + _, _, err = runContainer(config, "", "ps") + if err == nil { + t.Fatalf("runContainer should failed with invalid CpuShares") + } +} + func TestContainerState(t *testing.T) { if testing.Short() { return @@ -648,3 +581,185 @@ func TestContainerState(t *testing.T) { stdinW.Close() p.Wait() } + +func TestPassExtraFiles(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + + factory, err := libcontainer.New(rootfs, libcontainer.Cgroupfs) + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + var stdout bytes.Buffer + pipeout1, pipein1, err := os.Pipe() + pipeout2, pipein2, err := os.Pipe() + process := libcontainer.Process{ + Args: []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"}, + Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, + ExtraFiles: []*os.File{pipein1, pipein2}, + Stdin: nil, + Stdout: &stdout, + } + err = container.Start(&process) + if err != nil { + t.Fatal(err) + } + + waitProcess(&process, t) + + out := string(stdout.Bytes()) + // fd 5 is the directory handle for /proc/$$/fd + if out != "0 1 2 3 4 5" { + t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to init, got '%s'", out) + } + var buf = []byte{0} + _, err = pipeout1.Read(buf) + if err != nil { + t.Fatal(err) + } + out1 := string(buf) + if out1 != "1" { + t.Fatalf("expected first pipe to receive '1', got '%s'", out1) + } + + _, err = pipeout2.Read(buf) + if err != nil { + t.Fatal(err) + } + out2 := string(buf) + if out2 != "2" { + t.Fatalf("expected second pipe to receive '2', got '%s'", out2) + } +} + +func TestMountCmds(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + tmpDir, err := ioutil.TempDir("", "tmpdir") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + config := newTemplateConfig(rootfs) + config.Mounts = append(config.Mounts, &configs.Mount{ + Source: tmpDir, + Destination: filepath.Join(rootfs, "tmp"), + Device: "bind", + Flags: syscall.MS_BIND | syscall.MS_REC, + PremountCmds: []configs.Command{ + {Path: "touch", Args: []string{filepath.Join(tmpDir, "hello")}}, + {Path: "touch", Args: []string{filepath.Join(tmpDir, "world")}}, + }, + PostmountCmds: []configs.Command{ + {Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "hello"), filepath.Join(rootfs, "tmp", "hello-backup")}}, + {Path: "cp", Args: []string{filepath.Join(rootfs, "tmp", "world"), filepath.Join(rootfs, "tmp", "world-backup")}}, + }, + }) + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + pconfig := libcontainer.Process{ + Args: []string{"sh", "-c", "env"}, + Env: standardEnvironment, + } + err = container.Start(&pconfig) + if err != nil { + t.Fatal(err) + } + + // Wait for process + waitProcess(&pconfig, t) + + entries, err := ioutil.ReadDir(tmpDir) + if err != nil { + t.Fatal(err) + } + expected := []string{"hello", "hello-backup", "world", "world-backup"} + for i, e := range entries { + if e.Name() != expected[i] { + t.Errorf("Got(%s), expect %s", e.Name(), expected[i]) + } + } +} + +func TestSystemProperties(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + ok(t, err) + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + ok(t, err) + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + config.SystemProperties = map[string]string{ + "kernel.shmmni": "8192", + } + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + ok(t, err) + + container, err := factory.Create("test", config) + ok(t, err) + defer container.Destroy() + + var stdout bytes.Buffer + pconfig := libcontainer.Process{ + Args: []string{"sh", "-c", "cat /proc/sys/kernel/shmmni"}, + Env: standardEnvironment, + Stdin: nil, + Stdout: &stdout, + } + err = container.Start(&pconfig) + ok(t, err) + + // Wait for process + waitProcess(&pconfig, t) + + shmmniOutput := strings.TrimSpace(string(stdout.Bytes())) + if shmmniOutput != "8192" { + t.Fatalf("kernel.shmmni property expected to be 8192, but is %s", shmmniOutput) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/integration/execin_test.go b/vendor/src/github.com/docker/libcontainer/integration/execin_test.go index 252e6e415e43b..f81faf010a95f 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/execin_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/execin_test.go @@ -16,22 +16,16 @@ func TestExecIn(t *testing.T) { return } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) container, err := newContainer(config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() // Execute a first process in the container stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) process := &libcontainer.Process{ Args: []string{"cat"}, Env: standardEnvironment, @@ -40,9 +34,7 @@ func TestExecIn(t *testing.T) { err = container.Start(process) stdinR.Close() defer stdinW.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) buffers := newStdBuffers() ps := &libcontainer.Process{ @@ -53,12 +45,9 @@ func TestExecIn(t *testing.T) { Stderr: buffers.Stderr, } err = container.Start(ps) - if err != nil { - t.Fatal(err) - } - if _, err := ps.Wait(); err != nil { - t.Fatal(err) - } + ok(t, err) + _, err = ps.Wait() + ok(t, err) stdinW.Close() if _, err := process.Wait(); err != nil { t.Log(err) @@ -74,21 +63,15 @@ func TestExecInRlimit(t *testing.T) { return } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) container, err := newContainer(config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) process := &libcontainer.Process{ Args: []string{"cat"}, Env: standardEnvironment, @@ -97,9 +80,7 @@ func TestExecInRlimit(t *testing.T) { err = container.Start(process) stdinR.Close() defer stdinW.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) buffers := newStdBuffers() ps := &libcontainer.Process{ @@ -110,12 +91,9 @@ func TestExecInRlimit(t *testing.T) { Stderr: buffers.Stderr, } err = container.Start(ps) - if err != nil { - t.Fatal(err) - } - if _, err := ps.Wait(); err != nil { - t.Fatal(err) - } + ok(t, err) + _, err = ps.Wait() + ok(t, err) stdinW.Close() if _, err := process.Wait(); err != nil { t.Log(err) @@ -131,22 +109,16 @@ func TestExecInError(t *testing.T) { return } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) container, err := newContainer(config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() // Execute a first process in the container stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) process := &libcontainer.Process{ Args: []string{"cat"}, Env: standardEnvironment, @@ -160,9 +132,7 @@ func TestExecInError(t *testing.T) { t.Log(err) } }() - if err != nil { - t.Fatal(err) - } + ok(t, err) unexistent := &libcontainer.Process{ Args: []string{"unexistent"}, @@ -182,22 +152,16 @@ func TestExecInTTY(t *testing.T) { return } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) container, err := newContainer(config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() // Execute a first process in the container stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) process := &libcontainer.Process{ Args: []string{"cat"}, Env: standardEnvironment, @@ -206,9 +170,7 @@ func TestExecInTTY(t *testing.T) { err = container.Start(process) stdinR.Close() defer stdinW.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) var stdout bytes.Buffer ps := &libcontainer.Process{ @@ -221,21 +183,16 @@ func TestExecInTTY(t *testing.T) { io.Copy(&stdout, console) close(copy) }() - if err != nil { - t.Fatal(err) - } + ok(t, err) err = container.Start(ps) - if err != nil { - t.Fatal(err) - } + ok(t, err) select { case <-time.After(5 * time.Second): t.Fatal("Waiting for copy timed out") case <-copy: } - if _, err := ps.Wait(); err != nil { - t.Fatal(err) - } + _, err = ps.Wait() + ok(t, err) stdinW.Close() if _, err := process.Wait(); err != nil { t.Log(err) @@ -251,22 +208,16 @@ func TestExecInEnvironment(t *testing.T) { return } rootfs, err := newRootfs() - if err != nil { - t.Fatal(err) - } + ok(t, err) defer remove(rootfs) config := newTemplateConfig(rootfs) container, err := newContainer(config) - if err != nil { - t.Fatal(err) - } + ok(t, err) defer container.Destroy() // Execute a first process in the container stdinR, stdinW, err := os.Pipe() - if err != nil { - t.Fatal(err) - } + ok(t, err) process := &libcontainer.Process{ Args: []string{"cat"}, Env: standardEnvironment, @@ -275,9 +226,7 @@ func TestExecInEnvironment(t *testing.T) { err = container.Start(process) stdinR.Close() defer stdinW.Close() - if err != nil { - t.Fatal(err) - } + ok(t, err) buffers := newStdBuffers() process2 := &libcontainer.Process{ @@ -293,9 +242,7 @@ func TestExecInEnvironment(t *testing.T) { Stderr: buffers.Stderr, } err = container.Start(process2) - if err != nil { - t.Fatal(err) - } + ok(t, err) if _, err := process2.Wait(); err != nil { out := buffers.Stdout.String() t.Fatal(err, out) @@ -314,3 +261,80 @@ func TestExecInEnvironment(t *testing.T) { t.Fatalf("unexpected running process, output %q", out) } } + +func TestExecinPassExtraFiles(t *testing.T) { + if testing.Short() { + return + } + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + config := newTemplateConfig(rootfs) + container, err := newContainer(config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + // Execute a first process in the container + stdinR, stdinW, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + process := &libcontainer.Process{ + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + } + err = container.Start(process) + stdinR.Close() + defer stdinW.Close() + if err != nil { + t.Fatal(err) + } + + var stdout bytes.Buffer + pipeout1, pipein1, err := os.Pipe() + pipeout2, pipein2, err := os.Pipe() + inprocess := &libcontainer.Process{ + Args: []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"}, + Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, + ExtraFiles: []*os.File{pipein1, pipein2}, + Stdin: nil, + Stdout: &stdout, + } + err = container.Start(inprocess) + if err != nil { + t.Fatal(err) + } + + waitProcess(inprocess, t) + stdinW.Close() + waitProcess(process, t) + + out := string(stdout.Bytes()) + // fd 5 is the directory handle for /proc/$$/fd + if out != "0 1 2 3 4 5" { + t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to exec, got '%s'", out) + } + var buf = []byte{0} + _, err = pipeout1.Read(buf) + if err != nil { + t.Fatal(err) + } + out1 := string(buf) + if out1 != "1" { + t.Fatalf("expected first pipe to receive '1', got '%s'", out1) + } + + _, err = pipeout2.Read(buf) + if err != nil { + t.Fatal(err) + } + out2 := string(buf) + if out2 != "2" { + t.Fatalf("expected second pipe to receive '2', got '%s'", out2) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/integration/init_test.go b/vendor/src/github.com/docker/libcontainer/integration/init_test.go index f11834de3422b..1f75ef525e2b1 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/init_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/init_test.go @@ -21,7 +21,7 @@ func init() { if err != nil { log.Fatalf("unable to initialize for container: %s", err) } - if err := factory.StartInitialization(3); err != nil { + if err := factory.StartInitialization(); err != nil { log.Fatal(err) } } diff --git a/vendor/src/github.com/docker/libcontainer/integration/utils_test.go b/vendor/src/github.com/docker/libcontainer/integration/utils_test.go index cf4596864e5d0..263d89d3b5665 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/utils_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/utils_test.go @@ -6,8 +6,11 @@ import ( "io/ioutil" "os" "os/exec" + "path/filepath" + "runtime" "strings" "syscall" + "testing" "github.com/docker/libcontainer" "github.com/docker/libcontainer/configs" @@ -38,6 +41,14 @@ func (b *stdBuffers) String() string { return strings.Join(s, "|") } +// ok fails the test if an err is not nil. +func ok(t testing.TB, err error) { + if err != nil { + _, file, line, _ := runtime.Caller(1) + t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error()) + } +} + // newRootfs creates a new tmp directory and copies the busybox root filesystem func newRootfs() (string, error) { dir, err := ioutil.TempDir("", "") diff --git a/vendor/src/github.com/docker/libcontainer/label/label_selinux.go b/vendor/src/github.com/docker/libcontainer/label/label_selinux.go index 5983031ae0019..7bc40ddde2542 100644 --- a/vendor/src/github.com/docker/libcontainer/label/label_selinux.go +++ b/vendor/src/github.com/docker/libcontainer/label/label_selinux.go @@ -101,10 +101,22 @@ func SetFileCreateLabel(fileLabel string) error { // the MCS label should continue to be used. SELinux will use this field // to make sure the content can not be shared by other containes. func Relabel(path string, fileLabel string, relabel string) error { + exclude_path := []string{"/", "/usr", "/etc"} if fileLabel == "" { return nil } - if relabel == "z" { + for _, p := range exclude_path { + if path == p { + return fmt.Errorf("Relabeling of %s is not allowed", path) + } + } + if !strings.ContainsAny(relabel, "zZ") { + return nil + } + if strings.Contains(relabel, "z") && strings.Contains(relabel, "Z") { + return fmt.Errorf("Bad SELinux option z and Z can not be used together") + } + if strings.Contains(relabel, "z") { c := selinux.NewContext(fileLabel) c["level"] = "s0" fileLabel = c.Get() diff --git a/vendor/src/github.com/docker/libcontainer/label/label_selinux_test.go b/vendor/src/github.com/docker/libcontainer/label/label_selinux_test.go index 8629353f2471a..6ab0c67ca6f9f 100644 --- a/vendor/src/github.com/docker/libcontainer/label/label_selinux_test.go +++ b/vendor/src/github.com/docker/libcontainer/label/label_selinux_test.go @@ -87,3 +87,31 @@ func TestDuplicateLabel(t *testing.T) { t.Errorf("DisableSecOpt Failed level incorrect") } } +func TestRelabel(t *testing.T) { + testdir := "/tmp/test" + label := "system_u:system_r:svirt_sandbox_file_t:s0:c1,c2" + if err := Relabel(testdir, "", "z"); err != nil { + t.Fatal("Relabel with no label failed: %v", err) + } + if err := Relabel(testdir, label, ""); err != nil { + t.Fatal("Relabel with no relabel field failed: %v", err) + } + if err := Relabel(testdir, label, "z"); err != nil { + t.Fatal("Relabel shared failed: %v", err) + } + if err := Relabel(testdir, label, "Z"); err != nil { + t.Fatal("Relabel unshared failed: %v", err) + } + if err := Relabel(testdir, label, "zZ"); err == nil { + t.Fatal("Relabel with shared and unshared succeeded") + } + if err := Relabel("/etc", label, "zZ"); err == nil { + t.Fatal("Relabel /etc succeeded") + } + if err := Relabel("/", label, ""); err == nil { + t.Fatal("Relabel / succeeded") + } + if err := Relabel("/usr", label, "Z"); err == nil { + t.Fatal("Relabel /usr succeeded") + } +} diff --git a/vendor/src/github.com/docker/libcontainer/nsenter/README.md b/vendor/src/github.com/docker/libcontainer/nsenter/README.md index ac94cba05919f..d1a60ef9850c1 100644 --- a/vendor/src/github.com/docker/libcontainer/nsenter/README.md +++ b/vendor/src/github.com/docker/libcontainer/nsenter/README.md @@ -1,6 +1,25 @@ ## nsenter -The `nsenter` package registers a special init constructor that is called before the Go runtime has -a chance to boot. This provides us the ability to `setns` on existing namespaces and avoid the issues -that the Go runtime has with multiple threads. This constructor is only called if this package is -registered, imported, in your go application and the argv 0 is `nsenter`. +The `nsenter` package registers a special init constructor that is called before +the Go runtime has a chance to boot. This provides us the ability to `setns` on +existing namespaces and avoid the issues that the Go runtime has with multiple +threads. This constructor will be called if this package is registered, +imported, in your go application. + +The `nsenter` package will `import "C"` and it uses [cgo](https://golang.org/cmd/cgo/) +package. In cgo, if the import of "C" is immediately preceded by a comment, that comment, +called the preamble, is used as a header when compiling the C parts of the package. +So every time we import package `nsenter`, the C code function `nsexec()` would be +called. And package `nsenter` is now only imported in Docker execdriver, so every time +before we call `execdriver.Exec()`, that C code would run. + +`nsexec()` will first check the environment variable `_LIBCONTAINER_INITPID` +which will give the process of the container that should be joined. Namespaces fd will +be found from `/proc/[pid]/ns` and set by `setns` syscall. + +And then get the pipe number from `_LIBCONTAINER_INITPIPE`, error message could +be transfered through it. If tty is added, `_LIBCONTAINER_CONSOLE_PATH` will +have value and start a console for output. + +Finally, `nsexec()` will clone a child process , exit the parent process and let +the Go runtime take over. diff --git a/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go index 34e1f52118278..db27b8a4099a8 100644 --- a/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go +++ b/vendor/src/github.com/docker/libcontainer/nsenter/nsenter_test.go @@ -24,7 +24,7 @@ func TestNsenterAlivePid(t *testing.T) { Path: os.Args[0], Args: args, ExtraFiles: []*os.File{w}, - Env: []string{fmt.Sprintf("_LIBCONTAINER_INITPID=%d", os.Getpid())}, + Env: []string{fmt.Sprintf("_LIBCONTAINER_INITPID=%d", os.Getpid()), "_LIBCONTAINER_INITPIPE=3"}, } if err := cmd.Start(); err != nil { diff --git a/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c b/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c index e7658f3856fd4..d8e45f3cda2dc 100644 --- a/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c +++ b/vendor/src/github.com/docker/libcontainer/nsenter/nsexec.c @@ -66,7 +66,7 @@ void nsexec() const int num = sizeof(namespaces) / sizeof(char *); jmp_buf env; char buf[PATH_MAX], *val; - int i, tfd, child, len, consolefd = -1; + int i, tfd, child, len, pipenum, consolefd = -1; pid_t pid; char *console; @@ -81,6 +81,19 @@ void nsexec() exit(1); } + val = getenv("_LIBCONTAINER_INITPIPE"); + if (val == NULL) { + pr_perror("Child pipe not found"); + exit(1); + } + + pipenum = atoi(val); + snprintf(buf, sizeof(buf), "%d", pipenum); + if (strcmp(val, buf)) { + pr_perror("Unable to parse _LIBCONTAINER_INITPIPE"); + exit(1); + } + console = getenv("_LIBCONTAINER_CONSOLE_PATH"); if (console != NULL) { consolefd = open(console, O_RDWR); @@ -124,6 +137,8 @@ void nsexec() } if (setjmp(env) == 1) { + // Child + if (setsid() == -1) { pr_perror("setsid failed"); exit(1); @@ -149,7 +164,11 @@ void nsexec() // Finish executing, let the Go runtime take over. return; } + // Parent + // We must fork to actually enter the PID namespace, use CLONE_PARENT + // so the child can have the right parent, and we don't need to forward + // the child's exit code or resend its death signal. child = clone_parent(&env); if (child < 0) { pr_perror("Unable to fork"); @@ -158,7 +177,7 @@ void nsexec() len = snprintf(buf, sizeof(buf), "{ \"pid\" : %d }\n", child); - if (write(3, buf, len) != len) { + if (write(pipenum, buf, len) != len) { pr_perror("Unable to send a child pid"); kill(child, SIGKILL); exit(1); diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/README.md b/vendor/src/github.com/docker/libcontainer/nsinit/README.md index f2e66a866d894..98bed0e8e7f1d 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/README.md +++ b/vendor/src/github.com/docker/libcontainer/nsinit/README.md @@ -65,3 +65,48 @@ You can identify if a process is running in a container by looking to see if You may also specify an alternate root directory from where the `container.json` file is read and where the `state.json` file will be saved. + +### How to use? + +Currently nsinit has 9 commands. Type `nsinit -h` to list all of them. +And for every alternative command, you can also use `--help` to get more +detailed help documents. For example, `nsinit config --help`. + +`nsinit` cli application is implemented using [cli.go](https://github.com/codegangsta/cli). +Lots of details are handled in cli.go, so the implementation of `nsinit` itself +is very clean and clear. + +* **config** +It will generate a standard configuration file for a container. By default, it +will generate as the template file in [config.go](https://github.com/docker/libcontainer/blob/master/nsinit/config.go#L192). +It will modify the template if you have specified some configuration by options. +* **exec** +Starts a container and execute a new command inside it. Besides common options, it +has some special options as below. + - `--tty,-t`: allocate a TTY to the container. + - `--config`: you can specify a configuration file. By default, it will use + template configuration. + - `--id`: specify the ID for a container. By default, the id is "nsinit". + - `--user,-u`: set the user, uid, and/or gid for the process. By default the + value is "root". + - `--cwd`: set the current working dir. + - `--env`: set environment variables for the process. +* **init** +It's an internal command that is called inside the container's namespaces to +initialize the namespace and exec the user's process. It should not be called +externally. +* **oom** +Display oom notifications for a container, you should specify container id. +* **pause** +Pause the container's processes, you should specify container id. It will use +cgroup freeze subsystem to help. +* **unpause** +Unpause the container's processes. Same with `pause`. +* **stats** +Display statistics for the container, it will mainly show cgroup and network +statistics. +* **state** +Get the container's current state. You can also read the state from `state.json` + in your container_id folder. +* **help, h** +Shows a list of commands or help for one command. diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/config.go b/vendor/src/github.com/docker/libcontainer/nsinit/config.go index e50bb3c11dbbf..1eee9dd92998d 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/config.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/config.go @@ -43,6 +43,7 @@ var createFlags = []cli.Flag{ cli.StringFlag{Name: "veth-address", Usage: "veth ip address"}, cli.StringFlag{Name: "veth-gateway", Usage: "veth gateway address"}, cli.IntFlag{Name: "veth-mtu", Usage: "veth mtu"}, + cli.BoolFlag{Name: "cgroup", Usage: "mount the cgroup data for the container"}, } var configCommand = cli.Command{ @@ -187,6 +188,12 @@ func modify(config *configs.Config, context *cli.Context) { } config.Networks = append(config.Networks, network) } + if context.Bool("cgroup") { + config.Mounts = append(config.Mounts, &configs.Mount{ + Destination: "/sys/fs/cgroup", + Device: "cgroup", + }) + } } func getTemplate() *configs.Config { diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/exec.go b/vendor/src/github.com/docker/libcontainer/nsinit/exec.go index 9d302aa31e7d7..cf40a5951a1fc 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/exec.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/exec.go @@ -23,6 +23,7 @@ var execCommand = cli.Command{ Action: execAction, Flags: append([]cli.Flag{ cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"}, + cli.BoolFlag{Name: "systemd", Usage: "Use systemd for managing cgroups, if available"}, cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"}, cli.StringFlag{Name: "config", Value: "", Usage: "path to the configuration file"}, cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"}, diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/init.go b/vendor/src/github.com/docker/libcontainer/nsinit/init.go index 7b2cf1935d4df..c7506a0e99973 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/init.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/init.go @@ -20,7 +20,7 @@ var initCommand = cli.Command{ if err != nil { fatal(err) } - if err := factory.StartInitialization(3); err != nil { + if err := factory.StartInitialization(); err != nil { fatal(err) } panic("This line should never been executed") diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/oom.go b/vendor/src/github.com/docker/libcontainer/nsinit/oom.go index a59b753336f93..412534bcd67ec 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/oom.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/oom.go @@ -1,8 +1,7 @@ package main import ( - "log" - + log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" ) diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/pause.go b/vendor/src/github.com/docker/libcontainer/nsinit/pause.go index 89af0b6f73f53..40aace444b3fb 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/pause.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/pause.go @@ -1,8 +1,7 @@ package main import ( - "log" - + log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" ) diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/utils.go b/vendor/src/github.com/docker/libcontainer/nsinit/utils.go index 4deca76640bb4..92f0a9d9ef260 100644 --- a/vendor/src/github.com/docker/libcontainer/nsinit/utils.go +++ b/vendor/src/github.com/docker/libcontainer/nsinit/utils.go @@ -3,10 +3,12 @@ package main import ( "encoding/json" "fmt" + log "github.com/Sirupsen/logrus" "os" "github.com/codegangsta/cli" "github.com/docker/libcontainer" + "github.com/docker/libcontainer/cgroups/systemd" "github.com/docker/libcontainer/configs" ) @@ -29,7 +31,15 @@ func loadConfig(context *cli.Context) (*configs.Config, error) { } func loadFactory(context *cli.Context) (libcontainer.Factory, error) { - return libcontainer.New(context.GlobalString("root"), libcontainer.Cgroupfs) + cgm := libcontainer.Cgroupfs + if context.Bool("systemd") { + if systemd.UseSystemd() { + cgm = libcontainer.SystemdCgroups + } else { + log.Warn("systemd cgroup flag passed, but systemd support for managing cgroups is not available.") + } + } + return libcontainer.New(context.GlobalString("root"), cgm) } func getContainer(context *cli.Context) (libcontainer.Container, error) { diff --git a/vendor/src/github.com/docker/libcontainer/process.go b/vendor/src/github.com/docker/libcontainer/process.go index 82fcff8c4cbb5..7902d08ce4c42 100644 --- a/vendor/src/github.com/docker/libcontainer/process.go +++ b/vendor/src/github.com/docker/libcontainer/process.go @@ -23,7 +23,7 @@ type Process struct { Env []string // User will set the uid and gid of the executing process running inside the container - // local to the contaienr's user and group configuration. + // local to the container's user and group configuration. User string // Cwd will change the processes current working directory inside the container's rootfs. @@ -38,11 +38,14 @@ type Process struct { // Stderr is a pointer to a writer which receives the standard error stream. Stderr io.Writer + // ExtraFiles specifies additional open files to be inherited by the container + ExtraFiles []*os.File + // consolePath is the path to the console allocated to the container. consolePath string // Capabilities specify the capabilities to keep when executing the process inside the container - // All capbilities not specified will be dropped from the processes capability mask + // All capabilities not specified will be dropped from the processes capability mask Capabilities []string ops processOperations diff --git a/vendor/src/github.com/docker/libcontainer/process_linux.go b/vendor/src/github.com/docker/libcontainer/process_linux.go index 1c74b65490763..66411a8a9d256 100644 --- a/vendor/src/github.com/docker/libcontainer/process_linux.go +++ b/vendor/src/github.com/docker/libcontainer/process_linux.go @@ -119,6 +119,9 @@ func (p *setnsProcess) execSetns() error { // terminate sends a SIGKILL to the forked process for the setns routine then waits to // avoid the process becomming a zombie. func (p *setnsProcess) terminate() error { + if p.cmd.Process == nil { + return nil + } err := p.cmd.Process.Kill() if _, werr := p.wait(); err == nil { err = werr diff --git a/vendor/src/github.com/docker/libcontainer/rootfs_linux.go b/vendor/src/github.com/docker/libcontainer/rootfs_linux.go index ab1a9a5fcb7b8..d8c61e97a0bff 100644 --- a/vendor/src/github.com/docker/libcontainer/rootfs_linux.go +++ b/vendor/src/github.com/docker/libcontainer/rootfs_linux.go @@ -6,11 +6,14 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" + "path" "path/filepath" "strings" "syscall" "time" + "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/configs" "github.com/docker/libcontainer/label" ) @@ -24,9 +27,20 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) { return newSystemError(err) } for _, m := range config.Mounts { + for _, precmd := range m.PremountCmds { + if err := mountCmd(precmd); err != nil { + return newSystemError(err) + } + } if err := mountToRootfs(m, config.Rootfs, config.MountLabel); err != nil { return newSystemError(err) } + + for _, postcmd := range m.PostmountCmds { + if err := mountCmd(postcmd); err != nil { + return newSystemError(err) + } + } } if err := createDevices(config); err != nil { return newSystemError(err) @@ -62,6 +76,18 @@ func setupRootfs(config *configs.Config, console *linuxConsole) (err error) { return nil } +func mountCmd(cmd configs.Command) error { + + command := exec.Command(cmd.Path, cmd.Args[:]...) + command.Env = cmd.Env + command.Dir = cmd.Dir + if out, err := command.CombinedOutput(); err != nil { + return fmt.Errorf("%#v failed: %s: %v", cmd, string(out), err) + } + + return nil +} + func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { var ( dest = m.Destination @@ -72,11 +98,19 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { } switch m.Device { - case "proc", "mqueue", "sysfs": + case "proc", "sysfs": if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { return err } return syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), "") + case "mqueue": + if err := os.MkdirAll(dest, 0755); err != nil && !os.IsExist(err) { + return err + } + if err := syscall.Mount(m.Source, dest, m.Device, uintptr(m.Flags), ""); err != nil { + return err + } + return label.SetFileLabel(dest, mountLabel) case "tmpfs": stat, err := os.Stat(dest) if err != nil { @@ -126,6 +160,37 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error { return err } } + case "cgroup": + mounts, err := cgroups.GetCgroupMounts() + if err != nil { + return err + } + var binds []*configs.Mount + for _, mm := range mounts { + dir, err := mm.GetThisCgroupDir() + if err != nil { + return err + } + binds = append(binds, &configs.Mount{ + Device: "bind", + Source: filepath.Join(mm.Mountpoint, dir), + Destination: filepath.Join(m.Destination, strings.Join(mm.Subsystems, ",")), + Flags: syscall.MS_BIND | syscall.MS_REC | syscall.MS_RDONLY, + }) + } + tmpfs := &configs.Mount{ + Device: "tmpfs", + Destination: m.Destination, + Flags: syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV, + } + if err := mountToRootfs(tmpfs, rootfs, mountLabel); err != nil { + return err + } + for _, b := range binds { + if err := mountToRootfs(b, rootfs, mountLabel); err != nil { + return err + } + } default: return fmt.Errorf("unknown mount device %q to %q", m.Device, m.Destination) } @@ -240,9 +305,9 @@ func mknodDevice(dest string, node *configs.Device) error { } func prepareRoot(config *configs.Config) error { - flag := syscall.MS_PRIVATE | syscall.MS_REC - if config.NoPivotRoot { - flag = syscall.MS_SLAVE | syscall.MS_REC + flag := syscall.MS_SLAVE | syscall.MS_REC + if config.Privatefs { + flag = syscall.MS_PRIVATE | syscall.MS_REC } if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { return err @@ -355,3 +420,10 @@ func maskFile(path string) error { } return nil } + +// writeSystemProperty writes the value to a path under /proc/sys as determined from the key. +// For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward. +func writeSystemProperty(key, value string) error { + keyPath := strings.Replace(key, ".", "/", -1) + return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) +} diff --git a/vendor/src/github.com/docker/libcontainer/standard_init_linux.go b/vendor/src/github.com/docker/libcontainer/standard_init_linux.go index 282832b568ff2..251c09f696e64 100644 --- a/vendor/src/github.com/docker/libcontainer/standard_init_linux.go +++ b/vendor/src/github.com/docker/libcontainer/standard_init_linux.go @@ -64,6 +64,13 @@ func (l *linuxStandardInit) Init() error { if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil { return err } + + for key, value := range l.config.Config.SystemProperties { + if err := writeSystemProperty(key, value); err != nil { + return err + } + } + for _, path := range l.config.Config.ReadonlyPaths { if err := remountReadonly(path); err != nil { return err diff --git a/vendor/src/github.com/docker/libcontainer/system/setns_linux.go b/vendor/src/github.com/docker/libcontainer/system/setns_linux.go index 228e6ccd7f46a..a3c4cbb273d9c 100644 --- a/vendor/src/github.com/docker/libcontainer/system/setns_linux.go +++ b/vendor/src/github.com/docker/libcontainer/system/setns_linux.go @@ -12,8 +12,10 @@ import ( // We are declaring the macro here because the SETNS syscall does not exist in th stdlib var setNsMap = map[string]uintptr{ "linux/386": 346, + "linux/arm64": 268, "linux/amd64": 308, - "linux/arm": 374, + "linux/arm": 375, + "linux/ppc": 350, "linux/ppc64": 350, "linux/ppc64le": 350, "linux/s390x": 339, diff --git a/vendor/src/github.com/docker/libcontainer/system/syscall_linux_64.go b/vendor/src/github.com/docker/libcontainer/system/syscall_linux_64.go index 6840c3770f443..0816bf8281698 100644 --- a/vendor/src/github.com/docker/libcontainer/system/syscall_linux_64.go +++ b/vendor/src/github.com/docker/libcontainer/system/syscall_linux_64.go @@ -1,4 +1,4 @@ -// +build linux,amd64 linux,ppc64 linux,ppc64le linux,s390x +// +build linux,arm64 linux,amd64 linux,ppc linux,ppc64 linux,ppc64le linux,s390x package system diff --git a/vendor/src/github.com/docker/libcontainer/update-vendor.sh b/vendor/src/github.com/docker/libcontainer/update-vendor.sh index b68f5d46107f3..ab471872b75a2 100755 --- a/vendor/src/github.com/docker/libcontainer/update-vendor.sh +++ b/vendor/src/github.com/docker/libcontainer/update-vendor.sh @@ -43,7 +43,7 @@ clone() { clone git github.com/codegangsta/cli 1.1.0 clone git github.com/coreos/go-systemd v2 clone git github.com/godbus/dbus v2 -clone git github.com/Sirupsen/logrus v0.6.6 +clone git github.com/Sirupsen/logrus v0.7.3 clone git github.com/syndtr/gocapability 8e4cdcb # intentionally not vendoring Docker itself... that'd be a circle :) From 815b472a02dc0f593daee4006ce893fe17236b70 Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Mon, 4 May 2015 19:56:10 +0200 Subject: [PATCH 078/321] Add more ioutils tests. Closes #11595 Signed-off-by: Vincent Demeester --- pkg/ioutils/readers_test.go | 125 ++++++++++++++++++++++++++++++++++++ pkg/ioutils/writers_test.go | 24 +++++++ 2 files changed, 149 insertions(+) diff --git a/pkg/ioutils/readers_test.go b/pkg/ioutils/readers_test.go index 0af978e068812..b4cbfd95f6bca 100644 --- a/pkg/ioutils/readers_test.go +++ b/pkg/ioutils/readers_test.go @@ -2,11 +2,92 @@ package ioutils import ( "bytes" + "fmt" "io" "io/ioutil" + "strings" "testing" ) +// Implement io.Reader +type errorReader struct{} + +func (r *errorReader) Read(p []byte) (int, error) { + return 0, fmt.Errorf("Error reader always fail.") +} + +func TestReadCloserWrapperClose(t *testing.T) { + reader := strings.NewReader("A string reader") + wrapper := NewReadCloserWrapper(reader, func() error { + return fmt.Errorf("This will be called when closing") + }) + err := wrapper.Close() + if err == nil || !strings.Contains(err.Error(), "This will be called when closing") { + t.Fatalf("readCloserWrapper should have call the anonymous func and thus, fail.") + } +} + +func TestReaderErrWrapperReadOnError(t *testing.T) { + called := false + reader := &errorReader{} + wrapper := NewReaderErrWrapper(reader, func() { + called = true + }) + _, err := wrapper.Read([]byte{}) + if err == nil || !strings.Contains(err.Error(), "Error reader always fail.") { + t.Fatalf("readErrWrapper should returned an error") + } + if !called { + t.Fatalf("readErrWrapper should have call the anonymous function on failure") + } +} + +func TestReaderErrWrapperRead(t *testing.T) { + called := false + reader := strings.NewReader("a string reader.") + wrapper := NewReaderErrWrapper(reader, func() { + called = true // Should not be called + }) + // Read 20 byte (should be ok with the string above) + num, err := wrapper.Read(make([]byte, 20)) + if err != nil { + t.Fatal(err) + } + if num != 16 { + t.Fatalf("readerErrWrapper should have read 16 byte, but read %d", num) + } +} + +func TestNewBufReaderWithDrainbufAndBuffer(t *testing.T) { + reader, writer := io.Pipe() + + drainBuffer := make([]byte, 1024) + buffer := bytes.Buffer{} + bufreader := NewBufReaderWithDrainbufAndBuffer(reader, drainBuffer, &buffer) + + // Write everything down to a Pipe + // Usually, a pipe should block but because of the buffered reader, + // the writes will go through + done := make(chan bool) + go func() { + writer.Write([]byte("hello world")) + writer.Close() + done <- true + }() + + // Drain the reader *after* everything has been written, just to verify + // it is indeed buffering + <-done + + output, err := ioutil.ReadAll(bufreader) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(output, []byte("hello world")) { + t.Error(string(output)) + } +} + func TestBufReader(t *testing.T) { reader, writer := io.Pipe() bufreader := NewBufReader(reader) @@ -33,6 +114,50 @@ func TestBufReader(t *testing.T) { } } +func TestBufReaderCloseWithNonReaderCloser(t *testing.T) { + reader := strings.NewReader("buffer") + bufreader := NewBufReader(reader) + + if err := bufreader.Close(); err != nil { + t.Fatal(err) + } + +} + +// implements io.ReadCloser +type simpleReaderCloser struct{} + +func (r *simpleReaderCloser) Read(p []byte) (n int, err error) { + return 0, nil +} + +func (r *simpleReaderCloser) Close() error { + return nil +} + +func TestBufReaderCloseWithReaderCloser(t *testing.T) { + reader := &simpleReaderCloser{} + bufreader := NewBufReader(reader) + + err := bufreader.Close() + if err != nil { + t.Fatal(err) + } + +} + +func TestHashData(t *testing.T) { + reader := strings.NewReader("hash-me") + actual, err := HashData(reader) + if err != nil { + t.Fatal(err) + } + expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa" + if actual != expected { + t.Fatalf("Expecting %s, got %s", expected, actual) + } +} + type repeatedReader struct { readCount int maxReads int diff --git a/pkg/ioutils/writers_test.go b/pkg/ioutils/writers_test.go index 80d7f7f795452..564b1cd4f5f79 100644 --- a/pkg/ioutils/writers_test.go +++ b/pkg/ioutils/writers_test.go @@ -6,6 +6,30 @@ import ( "testing" ) +func TestWriteCloserWrapperClose(t *testing.T) { + called := false + writer := bytes.NewBuffer([]byte{}) + wrapper := NewWriteCloserWrapper(writer, func() error { + called = true + return nil + }) + if err := wrapper.Close(); err != nil { + t.Fatal(err) + } + if !called { + t.Fatalf("writeCloserWrapper should have call the anonymous function.") + } +} + +func TestNopWriteCloser(t *testing.T) { + writer := bytes.NewBuffer([]byte{}) + wrapper := NopWriteCloser(writer) + if err := wrapper.Close(); err != nil { + t.Fatal("NopWriteCloser always return nil on Close.") + } + +} + func TestNopWriter(t *testing.T) { nw := &NopWriter{} l, err := nw.Write([]byte{'c'}) From ea6649e701cf04b90fcc4a1abb29337d99b7af70 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 4 May 2015 20:17:41 +0200 Subject: [PATCH 079/321] Add runcom to maintainers.people Signed-off-by: Antonio Murdaca --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5c02fa671be1c..5cf0757dd057d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -345,6 +345,7 @@ made through a pull request. "icecrime", "jfrazelle", "lk4d4", + "runcom", "tibor", "unclejack", "vbatts", @@ -592,6 +593,11 @@ made through a pull request. Email = "mary.anthony@docker.com" GitHub = "moxiegirl" + [people.runcom] + Name = "Antonio Murdaca" + Email = "me@runcom.ninja" + GitHub = "runcom" + [people.sday] Name = "Stephen Day" Email = "stephen.day@docker.com" From 7d371c0b470334189720840854b2d5acbb1c7909 Mon Sep 17 00:00:00 2001 From: mauriyouth Date: Sat, 2 May 2015 17:29:00 +0200 Subject: [PATCH 080/321] Make /etc/hosts, /etc/resolv.conf, /etc/hostname read only if --read-only is enable Signed-off-by: Antonio Murdaca --- daemon/volumes.go | 6 ++-- integration-cli/docker_cli_run_test.go | 46 +++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/daemon/volumes.go b/daemon/volumes.go index ea117a1e3fa60..49fc81255c9da 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -241,13 +241,13 @@ func validMountMode(mode string) bool { func (container *Container) specialMounts() []execdriver.Mount { var mounts []execdriver.Mount if container.ResolvConfPath != "" { - mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true}) + mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: !container.hostConfig.ReadonlyRootfs, Private: true}) } if container.HostnamePath != "" { - mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true}) + mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: !container.hostConfig.ReadonlyRootfs, Private: true}) } if container.HostsPath != "" { - mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true}) + mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: !container.hostConfig.ReadonlyRootfs, Private: true}) } return mounts } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 0cf5c31eeeac9..cc4c9988ab620 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2951,7 +2951,15 @@ func (s *DockerSuite) TestRunContainerWithWritableRootfs(c *check.C) { func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *check.C) { testRequires(c, NativeExecDriver) - out, err := exec.Command(dockerBinary, "run", "--read-only", "--rm", "busybox", "touch", "/file").CombinedOutput() + for _, f := range []string{"/file", "/etc/hosts", "/etc/resolv.conf", "/etc/hostname"} { + testReadOnlyFile(f, c) + } +} + +func testReadOnlyFile(filename string, c *check.C) { + testRequires(c, NativeExecDriver) + + out, err := exec.Command(dockerBinary, "run", "--read-only", "--rm", "busybox", "touch", filename).CombinedOutput() if err == nil { c.Fatal("expected container to error on run with read only error") } @@ -2961,6 +2969,42 @@ func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *check.C) { } } +func (s *DockerSuite) TestRunContainerWithReadonlyEtcHostsAndLinkedContainer(c *check.C) { + testRequires(c, NativeExecDriver) + + _, err := runCommand(exec.Command(dockerBinary, "run", "-d", "--name", "test-etc-hosts-ro-linked", "busybox", "top")) + c.Assert(err, check.IsNil) + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--read-only", "--link", "test-etc-hosts-ro-linked:testlinked", "busybox", "cat", "/etc/hosts")) + c.Assert(err, check.IsNil) + + if !strings.Contains(string(out), "testlinked") { + c.Fatal("Expected /etc/hosts to be updated even if --read-only enabled") + } +} + +func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithDnsFlag(c *check.C) { + testRequires(c, NativeExecDriver) + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--read-only", "--dns", "1.1.1.1", "busybox", "/bin/cat", "/etc/resolv.conf")) + c.Assert(err, check.IsNil) + + if !strings.Contains(string(out), "1.1.1.1") { + c.Fatal("Expected /etc/resolv.conf to be updated even if --read-only enabled and --dns flag used") + } +} + +func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithAddHostFlag(c *check.C) { + testRequires(c, NativeExecDriver) + + out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "--read-only", "--add-host", "testreadonly:127.0.0.1", "busybox", "/bin/cat", "/etc/hosts")) + c.Assert(err, check.IsNil) + + if !strings.Contains(string(out), "testreadonly") { + c.Fatal("Expected /etc/hosts to be updated even if --read-only enabled and --add-host flag used") + } +} + func (s *DockerSuite) TestRunVolumesFromRestartAfterRemoved(c *check.C) { out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "voltest", "-v", "/foo", "busybox")) if err != nil { From 8771cafab65e50d09d3590a7f22758e919b78fe4 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Sun, 3 May 2015 14:54:55 +0200 Subject: [PATCH 081/321] Add tests for API container delete Signed-off-by: Antonio Murdaca --- integration-cli/docker_api_containers_test.go | 110 ++++++++++++++++++ integration-cli/docker_cli_rm_test.go | 11 -- 2 files changed, 110 insertions(+), 11 deletions(-) diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 2651bd7ac8595..f9da6fbcbd316 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "io" "net/http" + "os" "os/exec" "strings" "time" @@ -1040,3 +1041,112 @@ func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) { c.Assert(err, check.IsNil) c.Assert(status, check.Equals, http.StatusNotFound) } + +func (s *DockerSuite) TestContainerApiDelete(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + stopCmd := exec.Command(dockerBinary, "stop", id) + _, err = runCommand(stopCmd) + c.Assert(err, check.IsNil) + + status, _, err := sockRequest("DELETE", "/containers/"+id, nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusNoContent) +} + +func (s *DockerSuite) TestContainerApiDeleteNotExist(c *check.C) { + status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusNotFound) + c.Assert(string(body), check.Matches, "no such id: doesnotexist\n") +} + +func (s *DockerSuite) TestContainerApiDeleteForce(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusNoContent) +} + +func (s *DockerSuite) TestContainerApiDeleteRemoveLinks(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "tlink1", "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + runCmd = exec.Command(dockerBinary, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top") + out, _, err = runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + id2 := strings.TrimSpace(out) + c.Assert(waitRun(id2), check.IsNil) + + links, err := inspectFieldJSON(id2, "HostConfig.Links") + c.Assert(err, check.IsNil) + + if links != "[\"/tlink1:/tlink2/tlink1\"]" { + c.Fatal("expected to have links between containers") + } + + status, _, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil) + c.Assert(err, check.IsNil) + c.Assert(status, check.Equals, http.StatusNoContent) + + linksPostRm, err := inspectFieldJSON(id2, "HostConfig.Links") + c.Assert(err, check.IsNil) + + if linksPostRm != "null" { + c.Fatal("call to api deleteContainer links should have removed the specified links") + } +} + +func (s *DockerSuite) TestContainerApiDeleteConflict(c *check.C) { + runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + status, _, err := sockRequest("DELETE", "/containers/"+id, nil) + c.Assert(status, check.Equals, http.StatusConflict) + c.Assert(err, check.IsNil) +} + +func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { + testRequires(c, SameHostDaemon) + + runCmd := exec.Command(dockerBinary, "run", "-d", "-v", "/testvolume", "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + vol, err := inspectFieldMap(id, "Volumes", "/testvolume") + c.Assert(err, check.IsNil) + + _, err = os.Stat(vol) + c.Assert(err, check.IsNil) + + status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil) + c.Assert(status, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + if _, err := os.Stat(vol); !os.IsNotExist(err) { + c.Fatalf("expected to get ErrNotExist error, got %v", err) + } +} diff --git a/integration-cli/docker_cli_rm_test.go b/integration-cli/docker_cli_rm_test.go index b8d1b843d1502..ba4e0e6bfd417 100644 --- a/integration-cli/docker_cli_rm_test.go +++ b/integration-cli/docker_cli_rm_test.go @@ -1,7 +1,6 @@ package main import ( - "net/http" "os" "os/exec" "strings" @@ -54,16 +53,6 @@ func (s *DockerSuite) TestRmRunningContainer(c *check.C) { } -func (s *DockerSuite) TestRmRunningContainerCheckError409(c *check.C) { - - createRunningContainer(c, "foo") - - endpoint := "/containers/foo" - status, _, err := sockRequest("DELETE", endpoint, nil) - c.Assert(status, check.Equals, http.StatusConflict) - c.Assert(err, check.IsNil) -} - func (s *DockerSuite) TestRmForceRemoveRunningContainer(c *check.C) { createRunningContainer(c, "foo") From dca9e02b15a3757272c90ec4cf0cc2b052a25fe3 Mon Sep 17 00:00:00 2001 From: wlan0 Date: Mon, 4 May 2015 14:39:48 -0700 Subject: [PATCH 082/321] Add log opts flag to pass in logging options Signed-off-by: wlan0 --- daemon/config.go | 1 + docker/daemon.go | 3 +++ docs/sources/reference/run.md | 4 +++ opts/opts.go | 51 +++++++++++++++++++++++++++++++++++ opts/opts_test.go | 35 ++++++++++++++++++++++++ runconfig/parse.go | 18 ++++++++++++- 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/daemon/config.go b/daemon/config.go index 5fe01e5c7cb6b..cd555cfc887a9 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -80,6 +80,7 @@ func (config *Config) InstallFlags() { opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers") flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Default driver for container logs") flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic") + opts.LogOptsVar(config.LogConfig.Config, []string{"-log-opt"}, "Set log driver options") } func getDefaultNetworkMtu() int { diff --git a/docker/daemon.go b/docker/daemon.go index 55cb090d67223..06bdbc0fc6275 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -32,6 +32,9 @@ var ( ) func init() { + if daemonCfg.LogConfig.Config == nil { + daemonCfg.LogConfig.Config = make(map[string]string) + } daemonCfg.InstallFlags() registryCfg.InstallFlags() } diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 0145828024d07..ff6cfa80bae52 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -849,6 +849,10 @@ command is not available for this logging driver Journald logging driver for Docker. Writes log messages to journald; the container id will be stored in the journal's `CONTAINER_ID` field. `docker logs` command is not available for this logging driver. For detailed information on working with this logging driver, see [the journald logging driver](reference/logging/journald) reference documentation. +#### Log Opts : + +Logging options for configuring a log driver. The following log options are supported: [none] + ## Overriding Dockerfile image defaults When a developer builds an image from a [*Dockerfile*](/reference/builder) diff --git a/opts/opts.go b/opts/opts.go index 1db454736e5bd..3801596631649 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -28,6 +28,14 @@ func ListVar(values *[]string, names []string, usage string) { flag.Var(newListOptsRef(values, nil), names, usage) } +func MapVar(values map[string]string, names []string, usage string) { + flag.Var(newMapOpt(values, nil), names, usage) +} + +func LogOptsVar(values map[string]string, names []string, usage string) { + flag.Var(newMapOpt(values, ValidateLogOpts), names, usage) +} + func HostListVar(values *[]string, names []string, usage string) { flag.Var(newListOptsRef(values, ValidateHost), names, usage) } @@ -130,10 +138,53 @@ func (opts *ListOpts) Len() int { return len((*opts.values)) } +//MapOpts type +type MapOpts struct { + values map[string]string + validator ValidatorFctType +} + +func (opts *MapOpts) Set(value string) error { + if opts.validator != nil { + v, err := opts.validator(value) + if err != nil { + return err + } + value = v + } + vals := strings.SplitN(value, "=", 2) + if len(vals) == 1 { + (opts.values)[vals[0]] = "" + } else { + (opts.values)[vals[0]] = vals[1] + } + return nil +} + +func (opts *MapOpts) String() string { + return fmt.Sprintf("%v", map[string]string((opts.values))) +} + +func newMapOpt(values map[string]string, validator ValidatorFctType) *MapOpts { + return &MapOpts{ + values: values, + validator: validator, + } +} + // Validators type ValidatorFctType func(val string) (string, error) type ValidatorFctListType func(val string) ([]string, error) +func ValidateLogOpts(val string) (string, error) { + allowedKeys := map[string]string{} + vals := strings.Split(val, "=") + if allowedKeys[vals[0]] != "" { + return val, nil + } + return "", fmt.Errorf("%s is not a valid log opt", vals[0]) +} + func ValidateAttach(val string) (string, error) { s := strings.ToLower(val) for _, str := range []string{"stdin", "stdout", "stderr"} { diff --git a/opts/opts_test.go b/opts/opts_test.go index 8370926da5915..dfad430ac4276 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -1,6 +1,7 @@ package opts import ( + "fmt" "strings" "testing" ) @@ -28,6 +29,31 @@ func TestValidateIPAddress(t *testing.T) { } +func TestMapOpts(t *testing.T) { + tmpMap := make(map[string]string) + o := newMapOpt(tmpMap, logOptsValidator) + o.Set("max-size=1") + if o.String() != "map[max-size:1]" { + t.Errorf("%s != [map[max-size:1]", o.String()) + } + + o.Set("max-file=2") + if len(tmpMap) != 2 { + t.Errorf("map length %d != 2", len(tmpMap)) + } + + if tmpMap["max-file"] != "2" { + t.Errorf("max-file = %s != 2", tmpMap["max-file"]) + } + + if tmpMap["max-size"] != "1" { + t.Errorf("max-size = %s != 1", tmpMap["max-size"]) + } + if o.Set("dummy-val=3") == nil { + t.Errorf("validator is not being called") + } +} + func TestValidateMACAddress(t *testing.T) { if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil { t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err) @@ -152,3 +178,12 @@ func TestValidateExtraHosts(t *testing.T) { } } } + +func logOptsValidator(val string) (string, error) { + allowedKeys := map[string]string{"max-size": "1", "max-file": "2"} + vals := strings.Split(val, "=") + if allowedKeys[vals[0]] != "" { + return val, nil + } + return "", fmt.Errorf("invalid key %s", vals[0]) +} diff --git a/runconfig/parse.go b/runconfig/parse.go index ac5cdbf61aa0c..e944a644e3b3e 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -47,6 +47,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flCapDrop = opts.NewListOpts(nil) flSecurityOpt = opts.NewListOpts(nil) flLabelsFile = opts.NewListOpts(nil) + flLoggingOpts = opts.NewListOpts(nil) flNetwork = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container") flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container") @@ -95,6 +96,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities") cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options") cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options") + cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options") cmd.Require(flag.Min, 1) @@ -283,6 +285,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe return nil, nil, cmd, err } + loggingOpts, err := parseLoggingOpts(*flLoggingDriver, flLoggingOpts.GetAll()) + if err != nil { + return nil, nil, cmd, err + } + config := &Config{ Hostname: hostname, Domainname: domainname, @@ -335,7 +342,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe SecurityOpt: flSecurityOpt.GetAll(), ReadonlyRootfs: *flReadonlyRootfs, Ulimits: flUlimits.GetList(), - LogConfig: LogConfig{Type: *flLoggingDriver}, + LogConfig: LogConfig{Type: *flLoggingDriver, Config: loggingOpts}, CgroupParent: *flCgroupParent, } @@ -377,6 +384,15 @@ func convertKVStringsToMap(values []string) map[string]string { return result } +func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) { + loggingOptsMap := convertKVStringsToMap(loggingOpts) + if loggingDriver == "none" && len(loggingOpts) > 0 { + return map[string]string{}, fmt.Errorf("Invalid logging opts for driver %s", loggingDriver) + } + //TODO - validation step + return loggingOptsMap, nil +} + // ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect func ParseRestartPolicy(policy string) (RestartPolicy, error) { p := RestartPolicy{} From 377ed712d38e75d24ebfe2b5b91455a77bf8b086 Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 4 May 2015 14:56:23 -0700 Subject: [PATCH 083/321] Windows: graph\export.go filepath fixes Signed-off-by: John Howard --- graph/export.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/graph/export.go b/graph/export.go index ae061a8a0fdc8..c356a23225963 100644 --- a/graph/export.go +++ b/graph/export.go @@ -5,7 +5,7 @@ import ( "io" "io/ioutil" "os" - "path" + "path/filepath" "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/archive" @@ -83,7 +83,7 @@ func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error { } // write repositories, if there is something to write if len(rootRepoMap) > 0 { - f, err := os.OpenFile(path.Join(tempdir, "repositories"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + f, err := os.OpenFile(filepath.Join(tempdir, "repositories"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { f.Close() return err @@ -115,7 +115,7 @@ func (s *TagStore) ImageExport(imageExportConfig *ImageExportConfig) error { func (s *TagStore) exportImage(name, tempdir string) error { for n := name; n != ""; { // temporary directory - tmpImageDir := path.Join(tempdir, n) + tmpImageDir := filepath.Join(tempdir, n) if err := os.Mkdir(tmpImageDir, os.FileMode(0755)); err != nil { if os.IsExist(err) { return nil @@ -126,12 +126,12 @@ func (s *TagStore) exportImage(name, tempdir string) error { var version = "1.0" var versionBuf = []byte(version) - if err := ioutil.WriteFile(path.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil { + if err := ioutil.WriteFile(filepath.Join(tmpImageDir, "VERSION"), versionBuf, os.FileMode(0644)); err != nil { return err } // serialize json - json, err := os.Create(path.Join(tmpImageDir, "json")) + json, err := os.Create(filepath.Join(tmpImageDir, "json")) if err != nil { return err } @@ -148,7 +148,7 @@ func (s *TagStore) exportImage(name, tempdir string) error { } // serialize filesystem - fsTar, err := os.Create(path.Join(tmpImageDir, "layer.tar")) + fsTar, err := os.Create(filepath.Join(tmpImageDir, "layer.tar")) if err != nil { return err } From b30f14f06d3a6c289257cf29d2e967ba09900a1e Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 4 May 2015 15:05:54 -0700 Subject: [PATCH 084/321] Windows: First fix for graph\graph.go Signed-off-by: John Howard --- graph/graph.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/graph/graph.go b/graph/graph.go index 9b2d7c2ee9634..3afe63e01ace8 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -7,7 +7,6 @@ import ( "io" "io/ioutil" "os" - "path" "path/filepath" "runtime" "strings" @@ -23,6 +22,7 @@ import ( "github.com/docker/docker/pkg/progressreader" "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" ) @@ -42,7 +42,7 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) { return nil, err } // Create the root directory if it doesn't exists - if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) { + if err := system.MkdirAll(root, 0700); err != nil && !os.IsExist(err) { return nil, err } @@ -229,8 +229,8 @@ func (graph *Graph) TempLayerArchive(id string, sf *streamformatter.StreamFormat // Mktemp creates a temporary sub-directory inside the graph's filesystem. func (graph *Graph) Mktemp(id string) (string, error) { - dir := path.Join(graph.Root, "_tmp", stringid.GenerateRandomID()) - if err := os.MkdirAll(dir, 0700); err != nil { + dir := filepath.Join(graph.Root, "_tmp", stringid.GenerateRandomID()) + if err := system.MkdirAll(dir, 0700); err != nil { return "", err } return dir, nil @@ -290,28 +290,28 @@ func SetupInitLayer(initLayer string) error { parts := strings.Split(pth, "/") prev := "/" for _, p := range parts[1:] { - prev = path.Join(prev, p) - syscall.Unlink(path.Join(initLayer, prev)) + prev = filepath.Join(prev, p) + syscall.Unlink(filepath.Join(initLayer, prev)) } - if _, err := os.Stat(path.Join(initLayer, pth)); err != nil { + if _, err := os.Stat(filepath.Join(initLayer, pth)); err != nil { if os.IsNotExist(err) { - if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil { + if err := system.MkdirAll(filepath.Join(initLayer, filepath.Dir(pth)), 0755); err != nil { return err } switch typ { case "dir": - if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil { + if err := system.MkdirAll(filepath.Join(initLayer, pth), 0755); err != nil { return err } case "file": - f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755) + f, err := os.OpenFile(filepath.Join(initLayer, pth), os.O_CREATE, 0755) if err != nil { return err } f.Close() default: - if err := os.Symlink(typ, path.Join(initLayer, pth)); err != nil { + if err := os.Symlink(typ, filepath.Join(initLayer, pth)); err != nil { return err } } @@ -432,7 +432,7 @@ func (graph *Graph) Heads() (map[string]*image.Image, error) { } func (graph *Graph) ImageRoot(id string) string { - return path.Join(graph.Root, id) + return filepath.Join(graph.Root, id) } func (graph *Graph) Driver() graphdriver.Driver { From bb1ecde1648220871bbf627b8a844fc8501163ba Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 4 May 2015 15:14:39 -0700 Subject: [PATCH 085/321] Windows: Filepath in graph\load.go Signed-off-by: John Howard --- graph/load.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graph/load.go b/graph/load.go index d978b1ee8e8ce..313f5d23a689f 100644 --- a/graph/load.go +++ b/graph/load.go @@ -7,7 +7,7 @@ import ( "io" "io/ioutil" "os" - "path" + "path/filepath" "github.com/Sirupsen/logrus" "github.com/docker/docker/image" @@ -25,7 +25,7 @@ func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error { defer os.RemoveAll(tmpImageDir) var ( - repoDir = path.Join(tmpImageDir, "repo") + repoDir = filepath.Join(tmpImageDir, "repo") ) if err := os.Mkdir(repoDir, os.ModeDir); err != nil { @@ -58,7 +58,7 @@ func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error { } } - reposJSONFile, err := os.Open(path.Join(tmpImageDir, "repo", "repositories")) + reposJSONFile, err := os.Open(filepath.Join(tmpImageDir, "repo", "repositories")) if err != nil { if !os.IsNotExist(err) { return err @@ -87,13 +87,13 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error { if _, err := s.LookupImage(address); err != nil { logrus.Debugf("Loading %s", address) - imageJson, err := ioutil.ReadFile(path.Join(tmpImageDir, "repo", address, "json")) + imageJson, err := ioutil.ReadFile(filepath.Join(tmpImageDir, "repo", address, "json")) if err != nil { logrus.Debugf("Error reading json", err) return err } - layer, err := os.Open(path.Join(tmpImageDir, "repo", address, "layer.tar")) + layer, err := os.Open(filepath.Join(tmpImageDir, "repo", address, "layer.tar")) if err != nil { logrus.Debugf("Error reading embedded tar", err) return err From f42348e18f73d1d775d77ac75bc96466aae56d7c Mon Sep 17 00:00:00 2001 From: Arnaud Porterie Date: Mon, 10 Nov 2014 16:19:16 -0800 Subject: [PATCH 086/321] Add `--userland-proxy` daemon flag The `--userland-proxy` daemon flag makes it possible to rely on hairpin NAT and additional iptables routes instead of userland proxy for port publishing and inter-container communication. Usage of the userland proxy remains the default as hairpin NAT is unsupported by older kernels. Signed-off-by: Arnaud Porterie --- daemon/config.go | 1 + daemon/container.go | 1 + daemon/execdriver/driver.go | 1 + daemon/execdriver/native/create.go | 1 + daemon/network/settings.go | 1 + daemon/networkdriver/bridge/driver.go | 43 +++++++--- daemon/networkdriver/bridge/driver_test.go | 2 +- daemon/networkdriver/portmapper/mapper.go | 31 +++++--- .../networkdriver/portmapper/mapper_test.go | 12 +-- docs/man/docker.1.md | 15 ++-- docs/sources/articles/networking.md | 20 ++++- docs/sources/reference/commandline/cli.md | 3 +- integration-cli/docker_cli_nat_test.go | 78 ++++++++++++++----- integration-cli/docker_cli_run_test.go | 44 ++--------- pkg/iptables/firewalld_test.go | 2 +- pkg/iptables/iptables.go | 9 ++- pkg/iptables/iptables_test.go | 5 +- 17 files changed, 171 insertions(+), 98 deletions(-) diff --git a/daemon/config.go b/daemon/config.go index 43b08531b52e1..5fe01e5c7cb6b 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -79,6 +79,7 @@ func (config *Config) InstallFlags() { config.Ulimits = make(map[string]*ulimit.Ulimit) opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers") flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Default driver for container logs") + flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic") } func getDefaultNetworkMtu() int { diff --git a/daemon/container.go b/daemon/container.go index ef42295344c44..d74ac76ddb565 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -307,6 +307,7 @@ func populateCommand(c *Container, env []string) error { GlobalIPv6Address: network.GlobalIPv6Address, GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen, IPv6Gateway: network.IPv6Gateway, + HairpinMode: network.HairpinMode, } } case "container": diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index df5901ed02e7f..28b3ee1235c62 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -96,6 +96,7 @@ type NetworkInterface struct { LinkLocalIPv6Address string `json:"link_local_ipv6"` GlobalIPv6PrefixLen int `json:"global_ipv6_prefix_len"` IPv6Gateway string `json:"ipv6_gateway"` + HairpinMode bool `json:"hairpin_mode"` } // TODO Windows: Factor out ulimit.Rlimit diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index fa53621c47ff3..61730deb5412d 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -114,6 +114,7 @@ func (d *driver) createNetwork(container *configs.Config, c *execdriver.Command) Gateway: c.Network.Interface.Gateway, Type: "veth", Bridge: c.Network.Interface.Bridge, + HairpinMode: c.Network.Interface.HairpinMode, } if c.Network.Interface.GlobalIPv6Address != "" { vethNetwork.IPv6Address = fmt.Sprintf("%s/%d", c.Network.Interface.GlobalIPv6Address, c.Network.Interface.GlobalIPv6PrefixLen) diff --git a/daemon/network/settings.go b/daemon/network/settings.go index f3841f09b1845..91d61160a6b29 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -15,4 +15,5 @@ type Settings struct { Bridge string PortMapping map[string]map[string]string // Deprecated Ports nat.PortMap + HairpinMode bool } diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index 2fe04d2065592..a324d6b6e4584 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -8,6 +8,7 @@ import ( "net" "os" "os/exec" + "path/filepath" "strconv" "strings" "sync" @@ -83,6 +84,7 @@ var ( gatewayIPv6 net.IP portMapper *portmapper.PortMapper once sync.Once + hairpinMode bool defaultBindingIP = net.ParseIP("0.0.0.0") currentInterfaces = ifaces{c: make(map[string]*networkInterface)} @@ -100,6 +102,7 @@ type Config struct { EnableIptables bool EnableIpForward bool EnableIpMasq bool + EnableUserlandProxy bool DefaultIp net.IP Iface string IP string @@ -131,6 +134,8 @@ func InitDriver(config *Config) error { defaultBindingIP = config.DefaultIp } + hairpinMode = !config.EnableUserlandProxy + bridgeIface = config.Iface usingDefaultBridge := false if bridgeIface == "" { @@ -243,39 +248,46 @@ func InitDriver(config *Config) error { if config.EnableIpForward { // Enable IPv4 forwarding if err := ioutil.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("WARNING: unable to enable IPv4 forwarding: %s\n", err) + logrus.Warnf("Unable to enable IPv4 forwarding: %v", err) } if config.FixedCIDRv6 != "" { // Enable IPv6 forwarding if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("WARNING: unable to enable IPv6 default forwarding: %s\n", err) + logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err) } if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, 0644); err != nil { - logrus.Warnf("WARNING: unable to enable IPv6 all forwarding: %s\n", err) + logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err) } } } + if hairpinMode { + // Enable loopback adresses routing + sysPath := filepath.Join("/proc/sys/net/ipv4/conf", bridgeIface, "route_localnet") + if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil { + logrus.Warnf("Unable to enable local routing for hairpin mode: %v", err) + } + } + // We can always try removing the iptables if err := iptables.RemoveExistingChain("DOCKER", iptables.Nat); err != nil { return err } if config.EnableIptables { - _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat) + _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Nat, hairpinMode) if err != nil { return err } // call this on Firewalld reload - iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Nat) }) - - chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter) + iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Nat, hairpinMode) }) + chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, hairpinMode) if err != nil { return err } // call this on Firewalld reload - iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Filter) }) + iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, hairpinMode) }) portMapper.SetIptablesChain(chain) } @@ -374,6 +386,18 @@ func setupIPTables(addr net.Addr, icc, ipmasq bool) error { } } + // In hairpin mode, masquerade traffic from localhost + if hairpinMode { + masqueradeArgs := []string{"-t", "nat", "-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"} + if !iptables.Exists(iptables.Filter, "POSTROUTING", masqueradeArgs...) { + if output, err := iptables.Raw(append([]string{"-I", "POSTROUTING"}, masqueradeArgs...)...); err != nil { + return fmt.Errorf("Unable to masquerade local traffic: %s", err) + } else if len(output) != 0 { + return fmt.Errorf("Error iptables masquerade local traffic: %s", output) + } + } + } + // Accept all non-intercontainer outgoing packets outgoingArgs := []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"} if !iptables.Exists(iptables.Filter, "FORWARD", outgoingArgs...) { @@ -637,6 +661,7 @@ func Allocate(id, requestedMac, requestedIP, requestedIPv6 string) (*network.Set Bridge: bridgeIface, IPPrefixLen: maskSize, LinkLocalIPv6Address: localIPv6.String(), + HairpinMode: hairpinMode, } if globalIPv6Network != nil { @@ -722,7 +747,7 @@ func AllocatePort(id string, port nat.Port, binding nat.PortBinding) (nat.PortBi return nat.PortBinding{}, err } for i := 0; i < MaxAllocatedPortAttempts; i++ { - if host, err = portMapper.Map(container, ip, hostPort); err == nil { + if host, err = portMapper.Map(container, ip, hostPort, !hairpinMode); err == nil { break } // There is no point in immediately retrying to map an explicitly diff --git a/daemon/networkdriver/bridge/driver_test.go b/daemon/networkdriver/bridge/driver_test.go index d18882e664267..c82acb86a2031 100644 --- a/daemon/networkdriver/bridge/driver_test.go +++ b/daemon/networkdriver/bridge/driver_test.go @@ -177,7 +177,7 @@ func TestLinkContainers(t *testing.T) { } bridgeIface = "lo" - if _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter); err != nil { + if _, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter, false); err != nil { t.Fatal(err) } diff --git a/daemon/networkdriver/portmapper/mapper.go b/daemon/networkdriver/portmapper/mapper.go index 09952ba35b30b..f0c7a507df3d7 100644 --- a/daemon/networkdriver/portmapper/mapper.go +++ b/daemon/networkdriver/portmapper/mapper.go @@ -51,7 +51,7 @@ func (pm *PortMapper) SetIptablesChain(c *iptables.Chain) { pm.chain = c } -func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host net.Addr, err error) { +func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { pm.lock.Lock() defer pm.lock.Unlock() @@ -59,7 +59,6 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host m *mapping proto string allocatedHostPort int - proxy UserlandProxy ) switch container.(type) { @@ -75,7 +74,9 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host container: container, } - proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port) + if useProxy { + m.userlandProxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port) + } case *net.UDPAddr: proto = "udp" if allocatedHostPort, err = pm.Allocator.RequestPort(hostIP, proto, hostPort); err != nil { @@ -88,7 +89,9 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host container: container, } - proxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port) + if useProxy { + m.userlandProxy = NewProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port) + } default: return nil, ErrUnknownBackendAddressType } @@ -112,7 +115,9 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host cleanup := func() error { // need to undo the iptables rules before we return - proxy.Stop() + if m.userlandProxy != nil { + m.userlandProxy.Stop() + } pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { return err @@ -121,13 +126,15 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host return nil } - if err := proxy.Start(); err != nil { - if err := cleanup(); err != nil { - return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) + if m.userlandProxy != nil { + if err := m.userlandProxy.Start(); err != nil { + if err := cleanup(); err != nil { + return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) + } + return nil, err } - return nil, err } - m.userlandProxy = proxy + pm.currentMappings[key] = m return m.host, nil } @@ -154,7 +161,9 @@ func (pm *PortMapper) Unmap(host net.Addr) error { return ErrPortNotMapped } - data.userlandProxy.Stop() + if data.userlandProxy != nil { + data.userlandProxy.Stop() + } delete(pm.currentMappings, key) diff --git a/daemon/networkdriver/portmapper/mapper_test.go b/daemon/networkdriver/portmapper/mapper_test.go index 729fe56075ed9..b908db2811b15 100644 --- a/daemon/networkdriver/portmapper/mapper_test.go +++ b/daemon/networkdriver/portmapper/mapper_test.go @@ -44,22 +44,22 @@ func TestMapPorts(t *testing.T) { return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) } - if host, err := pm.Map(srcAddr1, dstIp1, 80); err != nil { + if host, err := pm.Map(srcAddr1, dstIp1, 80, true); err != nil { t.Fatalf("Failed to allocate port: %s", err) } else if !addrEqual(dstAddr1, host) { t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) } - if _, err := pm.Map(srcAddr1, dstIp1, 80); err == nil { + if _, err := pm.Map(srcAddr1, dstIp1, 80, true); err == nil { t.Fatalf("Port is in use - mapping should have failed") } - if _, err := pm.Map(srcAddr2, dstIp1, 80); err == nil { + if _, err := pm.Map(srcAddr2, dstIp1, 80, true); err == nil { t.Fatalf("Port is in use - mapping should have failed") } - if _, err := pm.Map(srcAddr2, dstIp2, 80); err != nil { + if _, err := pm.Map(srcAddr2, dstIp2, 80, true); err != nil { t.Fatalf("Failed to allocate port: %s", err) } @@ -127,14 +127,14 @@ func TestMapAllPortsSingleInterface(t *testing.T) { for i := 0; i < 10; i++ { start, end := pm.Allocator.Begin, pm.Allocator.End for i := start; i < end; i++ { - if host, err = pm.Map(srcAddr1, dstIp1, 0); err != nil { + if host, err = pm.Map(srcAddr1, dstIp1, 0, true); err != nil { t.Fatal(err) } hosts = append(hosts, host) } - if _, err := pm.Map(srcAddr1, dstIp1, start); err == nil { + if _, err := pm.Map(srcAddr1, dstIp1, start, true); err == nil { t.Fatalf("Port %d should be bound but is not", start) } diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index 4e7cafe4661d5..8b7b06709e59f 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -53,6 +53,9 @@ To see the man page for a command run **man docker **. **-e**, **--exec-driver**="" Force Docker to use specific exec driver. Default is `native`. +**--exec-opt**=[] + Set exec driver options. See EXEC DRIVER OPTIONS. + **--fixed-cidr**="" IPv4 subnet for fixed IPs (e.g., 10.20.0.0/16); this subnet must be nested in the bridge subnet (which is defined by \-b or \-\-bip) @@ -111,6 +114,9 @@ unix://[/path/to/socket] to use. **-s**, **--storage-driver**="" Force the Docker runtime to use a specific storage driver. +**--selinux-enabled**=*true*|*false* + Enable selinux support. Default is false. SELinux does not presently support the BTRFS storage driver. + **--storage-opt**=[] Set storage driver options. See STORAGE DRIVER OPTIONS. @@ -121,15 +127,12 @@ unix://[/path/to/socket] to use. Use TLS and verify the remote (daemon: verify client, client: verify daemon). Default is false. +**--userland-proxy**=*true*|*false* + Rely on a userland proxy implementation for inter-container and outside-to-container loopback communications. Default is true. + **-v**, **--version**=*true*|*false* Print version information and quit. Default is false. -**--exec-opt**=[] - Set exec driver options. See EXEC DRIVER OPTIONS. - -**--selinux-enabled**=*true*|*false* - Enable selinux support. Default is false. SELinux does not presently support the BTRFS storage driver. - # COMMANDS **attach** Attach to a running container diff --git a/docs/sources/articles/networking.md b/docs/sources/articles/networking.md index 823b450c75656..5ee1d7e6ba44f 100644 --- a/docs/sources/articles/networking.md +++ b/docs/sources/articles/networking.md @@ -93,6 +93,9 @@ server when it starts up, and cannot be changed once it is running: * `--mtu=BYTES` — see [Customizing docker0](#docker0) + * `--userland-proxy=true|false` — see + [Binding container ports](#binding-ports) + There are two networking options that can be supplied either at startup or when `docker run` is invoked. When provided at startup, set the default value that `docker run` will later use if the options are not @@ -399,7 +402,7 @@ machine that the Docker server creates when it starts: ... Chain POSTROUTING (policy ACCEPT) target prot opt source destination - MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16 + MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0 ... But if you want containers to accept incoming connections, you will need @@ -452,6 +455,21 @@ address, you can edit your system-wide Docker server settings and add the option `--ip=IP_ADDRESS`. Remember to restart your Docker server after editing this setting. +> **Note**: +> With hairpin NAT enabled (`--userland-proxy=false`), containers port exposure +> is achieved purely through iptables rules, and no attempt to bind the exposed +> port is ever made. This means that nothing prevents shadowing a previously +> listening service outside of Docker through exposing the same port for a +> container. In such conflicting situation, Docker created iptables rules will +> take precedence and route to the container. + +The `--userland-proxy` parameter, true by default, provides a userland +implementation for inter-container and outside-to-container communication. When +disabled, Docker uses both an additional `MASQUERADE` iptable rule and the +`net.ipv4.route_localnet` kernel parameter which allow the host machine to +connect to a local container exposed port through the commonly used loopback +address: this alternative is preferred for performance reason. + Again, this topic is covered without all of these low-level networking details in the [Docker User Guide](/userguide/dockerlinks/) document if you would like to use that as your port redirection reference instead. diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index c69f0a170e2fa..7ef5d82eb50e9 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -149,6 +149,7 @@ expect an integer, and they can only be specified once. --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 --fixed-cidr="" IPv4 subnet for fixed IPs --fixed-cidr-v6="" IPv6 subnet for fixed IPs @@ -177,8 +178,8 @@ expect an integer, and they can only be specified once. --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 - --default-ulimit=[] Set default ulimit settings for containers. Options with [] may be specified multiple times. diff --git a/integration-cli/docker_cli_nat_test.go b/integration-cli/docker_cli_nat_test.go index 875b6540ab095..14237042ac2b6 100644 --- a/integration-cli/docker_cli_nat_test.go +++ b/integration-cli/docker_cli_nat_test.go @@ -4,14 +4,26 @@ import ( "fmt" "net" "os/exec" + "strconv" "strings" "github.com/go-check/check" ) -func (s *DockerSuite) TestNetworkNat(c *check.C) { - testRequires(c, SameHostDaemon, NativeExecDriver) +func startServerContainer(c *check.C, proto string, port int) string { + cmd := []string{"-d", "-p", fmt.Sprintf("%d:%d", port, port), "busybox", "nc", "-lp", strconv.Itoa(port)} + if proto == "udp" { + cmd = append(cmd, "-u") + } + name := "server" + if err := waitForContainer(name, cmd...); err != nil { + c.Fatalf("Failed to launch server container: %v", err) + } + return name +} + +func getExternalAddress(c *check.C) net.IP { iface, err := net.InterfaceByName("eth0") if err != nil { c.Skip(fmt.Sprintf("Test not running with `make test`. Interface eth0 not found: %v", err)) @@ -27,35 +39,65 @@ func (s *DockerSuite) TestNetworkNat(c *check.C) { c.Fatalf("Error retrieving the up for eth0: %s", err) } - runCmd := exec.Command(dockerBinary, "run", "-dt", "-p", "8080:8080", "busybox", "nc", "-lp", "8080") + return ifaceIP +} + +func getContainerLogs(c *check.C, containerID string) string { + runCmd := exec.Command(dockerBinary, "logs", containerID) out, _, err := runCommandWithOutput(runCmd) if err != nil { c.Fatal(out, err) } + return strings.Trim(out, "\r\n") +} - cleanedContainerID := strings.TrimSpace(out) - - runCmd = exec.Command(dockerBinary, "run", "busybox", "sh", "-c", fmt.Sprintf("echo hello world | nc -w 30 %s 8080", ifaceIP)) - out, _, err = runCommandWithOutput(runCmd) +func getContainerStatus(c *check.C, containerID string) string { + runCmd := exec.Command(dockerBinary, "inspect", "-f", "{{.State.Running}}", containerID) + out, _, err := runCommandWithOutput(runCmd) if err != nil { c.Fatal(out, err) } + return strings.Trim(out, "\r\n") +} - runCmd = exec.Command(dockerBinary, "logs", cleanedContainerID) - out, _, err = runCommandWithOutput(runCmd) - if err != nil { - c.Fatalf("failed to retrieve logs for container: %s, %v", out, err) - } +func (s *DockerSuite) TestNetworkNat(c *check.C) { + testRequires(c, SameHostDaemon, NativeExecDriver) + defer deleteAllContainers() - out = strings.Trim(out, "\r\n") + srv := startServerContainer(c, "tcp", 8080) - if expected := "hello world"; out != expected { - c.Fatalf("Unexpected output. Expected: %q, received: %q for iface %s", expected, out, ifaceIP) + // Spawn a new container which connects to the server through the + // interface address. + endpoint := getExternalAddress(c) + runCmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", fmt.Sprintf("echo hello world | nc -w 30 %s 8080", endpoint)) + if out, _, err := runCommandWithOutput(runCmd); err != nil { + c.Fatalf("Failed to connect to server: %v (output: %q)", err, string(out)) } - killCmd := exec.Command(dockerBinary, "kill", cleanedContainerID) - if out, _, err = runCommandWithOutput(killCmd); err != nil { - c.Fatalf("failed to kill container: %s, %v", out, err) + result := getContainerLogs(c, srv) + if expected := "hello world"; result != expected { + c.Fatalf("Unexpected output. Expected: %q, received: %q", expected, result) + } +} + +func (s *DockerSuite) TestNetworkLocalhostTCPNat(c *check.C) { + testRequires(c, SameHostDaemon, NativeExecDriver) + defer deleteAllContainers() + + srv := startServerContainer(c, "tcp", 8081) + + // Attempt to connect from the host to the listening container. + conn, err := net.Dial("tcp", "localhost:8081") + if err != nil { + c.Fatalf("Failed to connect to container (%v)", err) + } + if _, err := conn.Write([]byte("hello world\n")); err != nil { + c.Fatal(err) } + conn.Close() + result := getContainerLogs(c, srv) + if expected := "hello world"; result != expected { + c.Fatalf("Unexpected output. Expected: %q, received: %q", expected, result) + } } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 0cf5c31eeeac9..beec73dc4b630 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2197,49 +2197,19 @@ func (s *DockerSuite) TestRunPortInUse(c *check.C) { testRequires(c, SameHostDaemon) port := "1234" - l, err := net.Listen("tcp", ":"+port) - if err != nil { - c.Fatal(err) - } - defer l.Close() - cmd := exec.Command(dockerBinary, "run", "-d", "-p", port+":80", "busybox", "top") - out, _, err := runCommandWithOutput(cmd) - if err == nil { - c.Fatalf("Binding on used port must fail") - } - if !strings.Contains(out, "address already in use") { - c.Fatalf("Out must be about \"address already in use\", got %s", out) - } -} - -// https://github.com/docker/docker/issues/8428 -func (s *DockerSuite) TestRunPortProxy(c *check.C) { - testRequires(c, SameHostDaemon) - - port := "12345" cmd := exec.Command(dockerBinary, "run", "-d", "-p", port+":80", "busybox", "top") - out, _, err := runCommandWithOutput(cmd) if err != nil { - c.Fatalf("Failed to run and bind port %s, output: %s, error: %s", port, out, err) - } - - // connett for 10 times here. This will trigger 10 EPIPES in the child - // process and kill it when it writes to a closed stdout/stderr - for i := 0; i < 10; i++ { - net.Dial("tcp", fmt.Sprintf("0.0.0.0:%s", port)) + c.Fatalf("Fail to run listening container") } - listPs := exec.Command("sh", "-c", "ps ax | grep docker") - out, _, err = runCommandWithOutput(listPs) - if err != nil { - c.Errorf("list docker process failed with output %s, error %s", out, err) - } - if strings.Contains(out, "docker ") { - c.Errorf("Unexpected defunct docker process") + cmd = exec.Command(dockerBinary, "run", "-d", "-p", port+":80", "busybox", "top") + out, _, err = runCommandWithOutput(cmd) + if err == nil { + c.Fatalf("Binding on used port must fail") } - if !strings.Contains(out, "docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 12345") { - c.Errorf("Failed to find docker-proxy process, got %s", out) + if !strings.Contains(out, "port is already allocated") { + c.Fatalf("Out must be about \"port is already allocated\", got %s", out) } } diff --git a/pkg/iptables/firewalld_test.go b/pkg/iptables/firewalld_test.go index 3896007d646b3..ff92657b18eb6 100644 --- a/pkg/iptables/firewalld_test.go +++ b/pkg/iptables/firewalld_test.go @@ -14,7 +14,7 @@ func TestReloaded(t *testing.T) { var err error var fwdChain *Chain - fwdChain, err = NewChain("FWD", "lo", Filter) + fwdChain, err = NewChain("FWD", "lo", Filter, false) if err != nil { t.Fatal(err) } diff --git a/pkg/iptables/iptables.go b/pkg/iptables/iptables.go index 9983ec61ff9cf..64a45db992b4c 100644 --- a/pkg/iptables/iptables.go +++ b/pkg/iptables/iptables.go @@ -58,7 +58,7 @@ func initCheck() error { return nil } -func NewChain(name, bridge string, table Table) (*Chain, error) { +func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) { c := &Chain{ Name: name, Bridge: bridge, @@ -90,8 +90,10 @@ func NewChain(name, bridge string, table Table) (*Chain, error) { } output := []string{ "-m", "addrtype", - "--dst-type", "LOCAL", - "!", "--dst", "127.0.0.0/8"} + "--dst-type", "LOCAL"} + if !hairpinMode { + output = append(output, "!", "--dst", "127.0.0.0/8") + } if !Exists(Nat, "OUTPUT", output...) { if err := c.Output(Append, output...); err != nil { return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err) @@ -137,7 +139,6 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri "-p", proto, "-d", daddr, "--dport", strconv.Itoa(port), - "!", "-i", c.Bridge, "-j", "DNAT", "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil { return err diff --git a/pkg/iptables/iptables_test.go b/pkg/iptables/iptables_test.go index ced4262ce2941..3539bd5cf13ce 100644 --- a/pkg/iptables/iptables_test.go +++ b/pkg/iptables/iptables_test.go @@ -16,12 +16,12 @@ var filterChain *Chain func TestNewChain(t *testing.T) { var err error - natChain, err = NewChain(chainName, "lo", Nat) + natChain, err = NewChain(chainName, "lo", Nat, false) if err != nil { t.Fatal(err) } - filterChain, err = NewChain(chainName, "lo", Filter) + filterChain, err = NewChain(chainName, "lo", Filter, false) if err != nil { t.Fatal(err) } @@ -40,7 +40,6 @@ func TestForward(t *testing.T) { } dnatRule := []string{ - "!", "-i", filterChain.Bridge, "-d", ip.String(), "-p", proto, "--dport", strconv.Itoa(port), From 44de5fecce9dd194fade1b696e9297ac5c985754 Mon Sep 17 00:00:00 2001 From: Arnaud Porterie Date: Tue, 7 Apr 2015 16:53:39 -0700 Subject: [PATCH 087/321] Add DOCKER_USERLANDPROXY test variable Add an convenient way to switch --userland-proxy on and off in integration tests. Signed-off-by: Arnaud Porterie --- hack/make.sh | 1 + hack/make/.integration-daemon-start | 2 ++ integration-cli/docker_utils.go | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/hack/make.sh b/hack/make.sh index 4226af5bb2084..953278842b289 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -198,6 +198,7 @@ test_env() { DEST="$DEST" \ DOCKER_EXECDRIVER="$DOCKER_EXECDRIVER" \ DOCKER_GRAPHDRIVER="$DOCKER_GRAPHDRIVER" \ + DOCKER_USERLANDPROXY="$DOCKER_USERLANDPROXY" \ DOCKER_HOST="$DOCKER_HOST" \ GOPATH="$GOPATH" \ HOME="$DEST/fake-HOME" \ diff --git a/hack/make/.integration-daemon-start b/hack/make/.integration-daemon-start index 57fd525028e74..937979df3edd2 100644 --- a/hack/make/.integration-daemon-start +++ b/hack/make/.integration-daemon-start @@ -14,6 +14,7 @@ exec 41>&1 42>&2 export DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs} export DOCKER_EXECDRIVER=${DOCKER_EXECDRIVER:-native} +export DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true} if [ -z "$DOCKER_TEST_HOST" ]; then export DOCKER_HOST="unix://$(cd "$DEST" && pwd)/docker.sock" # "pwd" tricks to make sure $DEST is an absolute path, not a relative one @@ -23,6 +24,7 @@ if [ -z "$DOCKER_TEST_HOST" ]; then --storage-driver "$DOCKER_GRAPHDRIVER" \ --exec-driver "$DOCKER_EXECDRIVER" \ --pidfile "$DEST/docker.pid" \ + --userland-proxy="$DOCKER_USERLANDPROXY" \ &> "$DEST/docker.log" ) & trap "source '${MAKEDIR}/.integration-daemon-stop'" EXIT # make sure that if the script exits unexpectedly, we stop this daemon we just started diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index a1a845baff67d..3500d2f750999 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -37,6 +37,16 @@ type Daemon struct { storageDriver string execDriver string wait chan error + userlandProxy bool +} + +func enableUserlandProxy() bool { + if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { + if val, err := strconv.ParseBool(env); err != nil { + return val + } + } + return true } // NewDaemon returns a Daemon instance to be used for testing. @@ -58,11 +68,19 @@ func NewDaemon(c *check.C) *Daemon { c.Fatalf("Could not create %s/graph directory", daemonFolder) } + userlandProxy := true + if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { + if val, err := strconv.ParseBool(env); err != nil { + userlandProxy = val + } + } + return &Daemon{ c: c, folder: daemonFolder, storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"), execDriver: os.Getenv("DOCKER_EXECDRIVER"), + userlandProxy: userlandProxy, } } @@ -79,6 +97,7 @@ func (d *Daemon) Start(arg ...string) error { "--daemon", "--graph", fmt.Sprintf("%s/graph", d.folder), "--pidfile", fmt.Sprintf("%s/docker.pid", d.folder), + fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), } // If we don't explicitly set the log-level or debug flag(-D) then From 3b2c8f69fd7f1ca6a463b9bd2c2e006d9e13fe98 Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 4 May 2015 16:18:23 -0700 Subject: [PATCH 088/321] Windows: Fix filepath vs path in push.go Signed-off-by: John Howard --- graph/push.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/push.go b/graph/push.go index 1b33288d8fedf..e24ee9f54ed5c 100644 --- a/graph/push.go +++ b/graph/push.go @@ -7,7 +7,7 @@ import ( "io" "io/ioutil" "os" - "path" + "path/filepath" "sync" "github.com/Sirupsen/logrus" @@ -247,7 +247,7 @@ func (s *TagStore) pushRepository(r *registry.Session, out io.Writer, func (s *TagStore) pushImage(r *registry.Session, out io.Writer, imgID, ep string, token []string, sf *streamformatter.StreamFormatter) (checksum string, err error) { out = utils.NewWriteFlusher(out) - jsonRaw, err := ioutil.ReadFile(path.Join(s.graph.Root, imgID, "json")) + jsonRaw, err := ioutil.ReadFile(filepath.Join(s.graph.Root, imgID, "json")) if err != nil { return "", fmt.Errorf("Cannot retrieve the path for {%s}: %s", imgID, err) } From 1c3b697d60bc1a5f5197b0c3a432002cf03c8277 Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 4 May 2015 16:23:17 -0700 Subject: [PATCH 089/321] Windows: Fix filepath vs path in image.go Signed-off-by: John Howard --- image/image.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/image/image.go b/image/image.go index a34d2b940849f..e86ed2ff3f222 100644 --- a/image/image.go +++ b/image/image.go @@ -5,7 +5,7 @@ import ( "fmt" "io/ioutil" "os" - "path" + "path/filepath" "regexp" "strconv" "time" @@ -55,7 +55,7 @@ func LoadImage(root string) (*Image, error) { return nil, err } - if buf, err := ioutil.ReadFile(path.Join(root, "layersize")); err != nil { + if buf, err := ioutil.ReadFile(filepath.Join(root, "layersize")); err != nil { if !os.IsNotExist(err) { return nil, err } @@ -107,21 +107,21 @@ func (img *Image) SetGraph(graph Graph) { // SaveSize stores the current `size` value of `img` in the directory `root`. func (img *Image) SaveSize(root string) error { - if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil { + if err := ioutil.WriteFile(filepath.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil { return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err) } return nil } func (img *Image) SaveCheckSum(root, checksum string) error { - if err := ioutil.WriteFile(path.Join(root, "checksum"), []byte(checksum), 0600); err != nil { + if err := ioutil.WriteFile(filepath.Join(root, "checksum"), []byte(checksum), 0600); err != nil { return fmt.Errorf("Error storing checksum in %s/checksum: %s", root, err) } return nil } func (img *Image) GetCheckSum(root string) (string, error) { - cs, err := ioutil.ReadFile(path.Join(root, "checksum")) + cs, err := ioutil.ReadFile(filepath.Join(root, "checksum")) if err != nil { if os.IsNotExist(err) { return "", nil @@ -132,7 +132,7 @@ func (img *Image) GetCheckSum(root string) (string, error) { } func jsonPath(root string) string { - return path.Join(root, "json") + return filepath.Join(root, "json") } func (img *Image) RawJson() ([]byte, error) { From bba5fd8caa35d5edf8b68e593eafb277394a6989 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 5 May 2015 00:38:41 +0000 Subject: [PATCH 090/321] spelling fix from Marc MERLIN Signed-off-by: Sven Dowideit --- docs/sources/reference/builder.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/reference/builder.md b/docs/sources/reference/builder.md index 7dbe549237f13..fb4d5ce1d7b90 100644 --- a/docs/sources/reference/builder.md +++ b/docs/sources/reference/builder.md @@ -892,11 +892,11 @@ consider the following Dockerfile snippet: FROM ubuntu RUN mkdir /myvol - RUN echo "hello world" > /myvol/greating + RUN echo "hello world" > /myvol/greeting VOLUME /myvol This Dockerfile results in an image that causes `docker run`, to -create a new mount point at `/myvol` and copy the `greating` file +create a new mount point at `/myvol` and copy the `greeting` file into the newly created volume. > **Note**: From 6e8aa4e588d11735c70059a478fc0c7857cb382c Mon Sep 17 00:00:00 2001 From: Yuan Sun Date: Tue, 5 May 2015 08:51:13 +0800 Subject: [PATCH 091/321] Verify the no-trunc option for the search operation. Signed-off-by: Yuan Sun --- integration-cli/docker_cli_search_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/integration-cli/docker_cli_search_test.go b/integration-cli/docker_cli_search_test.go index c5ecdd03b9c11..da298a1e0c74c 100644 --- a/integration-cli/docker_cli_search_test.go +++ b/integration-cli/docker_cli_search_test.go @@ -63,6 +63,16 @@ func (s *DockerSuite) TestSearchCmdOptions(c *check.C) { c.Fatalf("failed to search on the central registry: %s, %v", outSearchCmd, err) } + searchCmdNotrunc := exec.Command(dockerBinary, "search", "--no-trunc=true", "busybox") + outSearchCmdNotrunc, _, err := runCommandWithOutput(searchCmdNotrunc) + if err != nil { + c.Fatalf("failed to search on the central registry: %s, %v", outSearchCmdNotrunc, err) + } + + if len(outSearchCmd) > len(outSearchCmdNotrunc) { + c.Fatalf("The no-trunc option can't take effect.") + } + searchCmdautomated := exec.Command(dockerBinary, "search", "--automated=true", "busybox") outSearchCmdautomated, exitCode, err := runCommandWithOutput(searchCmdautomated) //The busybox is a busybox base image, not an AUTOMATED image. if err != nil || exitCode != 0 { From a2c76912e04b087233577b46f96f446c5d13e2de Mon Sep 17 00:00:00 2001 From: Adria Casas Date: Thu, 23 Apr 2015 09:40:23 +0200 Subject: [PATCH 092/321] Rename int64Value to int64ValueOrZero. Signed-off-by: Adria Casas --- api/server/form.go | 2 +- api/server/form_test.go | 4 ++-- api/server/server.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/server/form.go b/api/server/form.go index af1cd2075e738..75584df065ddc 100644 --- a/api/server/form.go +++ b/api/server/form.go @@ -11,7 +11,7 @@ func boolValue(r *http.Request, k string) bool { return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none") } -func int64Value(r *http.Request, k string) int64 { +func int64ValueOrZero(r *http.Request, k string) int64 { val, err := strconv.ParseInt(r.FormValue(k), 10, 64) if err != nil { return 0 diff --git a/api/server/form_test.go b/api/server/form_test.go index 5cf6c82c14dfd..caa9f1757c057 100644 --- a/api/server/form_test.go +++ b/api/server/form_test.go @@ -33,7 +33,7 @@ func TestBoolValue(t *testing.T) { } } -func TestInt64Value(t *testing.T) { +func TestInt64ValueOrZero(t *testing.T) { cases := map[string]int64{ "": 0, "asdf": 0, @@ -47,7 +47,7 @@ func TestInt64Value(t *testing.T) { r, _ := http.NewRequest("POST", "", nil) r.Form = v - a := int64Value(r, "test") + a := int64ValueOrZero(r, "test") if a != e { t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a) } diff --git a/api/server/server.go b/api/server/server.go index d1540acb8449e..fbc844ad2efa0 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -1295,10 +1295,10 @@ func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *ht buildConfig.ForceRemove = boolValue(r, "forcerm") buildConfig.AuthConfig = authConfig buildConfig.ConfigFile = configFile - buildConfig.MemorySwap = int64Value(r, "memswap") - buildConfig.Memory = int64Value(r, "memory") - buildConfig.CpuShares = int64Value(r, "cpushares") - buildConfig.CpuQuota = int64Value(r, "cpuquota") + buildConfig.MemorySwap = int64ValueOrZero(r, "memswap") + buildConfig.Memory = int64ValueOrZero(r, "memory") + buildConfig.CpuShares = int64ValueOrZero(r, "cpushares") + buildConfig.CpuQuota = int64ValueOrZero(r, "cpuquota") buildConfig.CpuSetCpus = r.FormValue("cpusetcpus") buildConfig.CpuSetMems = r.FormValue("cpusetmems") From c7ca73c8309a49893fa54d0c604733f4c7b3cb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sankar=20=E0=AE=9A=E0=AE=99=E0=AF=8D=E0=AE=95=E0=AE=B0?= =?UTF-8?q?=E0=AF=8D?= Date: Tue, 5 May 2015 13:17:33 +0530 Subject: [PATCH 093/321] Added information about mailing lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sankar சங்கர் --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5603a55a7f178..4bb249eea017c 100644 --- a/README.md +++ b/README.md @@ -246,3 +246,9 @@ Docker on a Mac If you know of another project underway that should be listed here, please help us keep this list up-to-date by submitting a PR. + +Mailing Lists +============= + +* [Google group for Docker Users](https://groups.google.com/forum/#!forum/docker-user) +* [Google group for Docker development discussions](https://groups.google.com/forum/#!forum/docker-dev) From 5a6db4fd44c0e05c8b185174a9aa380c9cc4f719 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Tue, 5 May 2015 15:59:17 +0800 Subject: [PATCH 094/321] a few cleanups for client output Signed-off-by: Qiang Huang --- api/client/cli.go | 3 +-- api/client/help.go | 4 +--- api/client/inspect.go | 1 - api/client/kill.go | 9 ++++++--- api/client/pause.go | 9 ++++++--- api/client/restart.go | 9 ++++++--- api/client/rm.go | 9 ++++++--- api/client/rmi.go | 11 +++++++---- api/client/start.go | 6 +++++- api/client/stop.go | 9 ++++++--- api/client/unpause.go | 9 ++++++--- api/client/wait.go | 9 ++++++--- integration-cli/docker_cli_rm_test.go | 4 ++-- 13 files changed, 58 insertions(+), 34 deletions(-) diff --git a/api/client/cli.go b/api/client/cli.go index 600d4cc5a3abb..7d9610078cff4 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -90,8 +90,7 @@ func (cli *DockerCli) Cmd(args ...string) error { if len(args) > 0 { method, exists := cli.getMethod(args[0]) if !exists { - fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0]) - os.Exit(1) + return fmt.Errorf("docker: '%s' is not a docker command. See 'docker --help'.", args[0]) } return method(args[1:]...) } diff --git a/api/client/help.go b/api/client/help.go index e95387967a4b0..5dee9ba8a149a 100644 --- a/api/client/help.go +++ b/api/client/help.go @@ -2,7 +2,6 @@ package client import ( "fmt" - "os" flag "github.com/docker/docker/pkg/mflag" ) @@ -23,8 +22,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { if len(args) > 0 { method, exists := cli.getMethod(args[0]) if !exists { - fmt.Fprintf(cli.err, "docker: '%s' is not a docker command. See 'docker --help'.\n", args[0]) - os.Exit(1) + return fmt.Errorf("docker: '%s' is not a docker command. See 'docker --help'.", args[0]) } else { method("--help") return nil diff --git a/api/client/inspect.go b/api/client/inspect.go index d60d47ad353fe..8d9c086101ef1 100644 --- a/api/client/inspect.go +++ b/api/client/inspect.go @@ -26,7 +26,6 @@ func (cli *DockerCli) CmdInspect(args ...string) error { if *tmplStr != "" { var err error if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { - fmt.Fprintf(cli.err, "Template parsing error: %v\n", err) return StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } diff --git a/api/client/kill.go b/api/client/kill.go index 7ad1e561339e9..becff3b7e04dd 100644 --- a/api/client/kill.go +++ b/api/client/kill.go @@ -16,14 +16,17 @@ func (cli *DockerCli) CmdKill(args ...string) error { cmd.ParseFlags(args, true) - var encounteredError error + var errNames []string for _, name := range cmd.Args() { if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, nil)); err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to kill one or more containers") + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to kill containers: %v", errNames) + } + return nil } diff --git a/api/client/pause.go b/api/client/pause.go index 6c807410bad1d..2f8f3c83f13b2 100644 --- a/api/client/pause.go +++ b/api/client/pause.go @@ -14,14 +14,17 @@ func (cli *DockerCli) CmdPause(args ...string) error { cmd.Require(flag.Min, 1) cmd.ParseFlags(args, false) - var encounteredError error + var errNames []string for _, name := range cmd.Args() { if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, nil)); err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to pause container named %s", name) + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to pause containers: %v", errNames) + } + return nil } diff --git a/api/client/restart.go b/api/client/restart.go index 41b10676bd9db..c769fb6d27a82 100644 --- a/api/client/restart.go +++ b/api/client/restart.go @@ -21,15 +21,18 @@ func (cli *DockerCli) CmdRestart(args ...string) error { v := url.Values{} v.Set("t", strconv.Itoa(*nSeconds)) - var encounteredError error + var errNames []string for _, name := range cmd.Args() { _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, nil)) if err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to restart one or more containers") + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to restart containers: %v", errNames) + } + return nil } diff --git a/api/client/rm.go b/api/client/rm.go index d99f32e2fda9c..e6f3aeaeba46e 100644 --- a/api/client/rm.go +++ b/api/client/rm.go @@ -32,7 +32,7 @@ func (cli *DockerCli) CmdRm(args ...string) error { val.Set("force", "1") } - var encounteredError error + var errNames []string for _, name := range cmd.Args() { if name == "" { return fmt.Errorf("Container name cannot be empty") @@ -42,10 +42,13 @@ func (cli *DockerCli) CmdRm(args ...string) error { _, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil)) if err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to remove one or more containers") + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to remove containers: %v", errNames) + } + return nil } diff --git a/api/client/rmi.go b/api/client/rmi.go index a8590dc8203fd..36f2036d13614 100644 --- a/api/client/rmi.go +++ b/api/client/rmi.go @@ -29,17 +29,17 @@ func (cli *DockerCli) CmdRmi(args ...string) error { v.Set("noprune", "1") } - var encounteredError error + var errNames []string for _, name := range cmd.Args() { rdr, _, err := cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, nil) if err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to remove one or more images") + errNames = append(errNames, name) } else { dels := []types.ImageDelete{} if err := json.NewDecoder(rdr).Decode(&dels); err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to remove one or more images") + errNames = append(errNames, name) continue } @@ -52,5 +52,8 @@ func (cli *DockerCli) CmdRmi(args ...string) error { } } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to remove images: %v", errNames) + } + return nil } diff --git a/api/client/start.go b/api/client/start.go index b290524cafc7f..55f307f522a02 100644 --- a/api/client/start.go +++ b/api/client/start.go @@ -120,6 +120,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { } var encounteredError error + var errNames []string for _, name := range cmd.Args() { _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil)) if err != nil { @@ -127,7 +128,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { // attach and openStdin is false means it could be starting multiple containers // when a container start failed, show the error message and start next fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to start one or more containers") + errNames = append(errNames, name) } else { encounteredError = err } @@ -138,6 +139,9 @@ func (cli *DockerCli) CmdStart(args ...string) error { } } + if len(errNames) > 0 { + encounteredError = fmt.Errorf("Error: failed to start containers: %v", errNames) + } if encounteredError != nil { return encounteredError } diff --git a/api/client/stop.go b/api/client/stop.go index 08a1f5ba15082..9551911ffd7bc 100644 --- a/api/client/stop.go +++ b/api/client/stop.go @@ -23,15 +23,18 @@ func (cli *DockerCli) CmdStop(args ...string) error { v := url.Values{} v.Set("t", strconv.Itoa(*nSeconds)) - var encounteredError error + var errNames []string for _, name := range cmd.Args() { _, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, nil)) if err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to stop one or more containers") + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to stop containers: %v", errNames) + } + return nil } diff --git a/api/client/unpause.go b/api/client/unpause.go index bcecb46336cd5..dceeb23af9025 100644 --- a/api/client/unpause.go +++ b/api/client/unpause.go @@ -14,14 +14,17 @@ func (cli *DockerCli) CmdUnpause(args ...string) error { cmd.Require(flag.Min, 1) cmd.ParseFlags(args, false) - var encounteredError error + var errNames []string for _, name := range cmd.Args() { if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, nil)); err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name) + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to unpause containers: %v", errNames) + } + return nil } diff --git a/api/client/wait.go b/api/client/wait.go index 8f34b24521ee7..bfec19e24b657 100644 --- a/api/client/wait.go +++ b/api/client/wait.go @@ -17,15 +17,18 @@ func (cli *DockerCli) CmdWait(args ...string) error { cmd.ParseFlags(args, true) - var encounteredError error + var errNames []string for _, name := range cmd.Args() { status, err := waitForExit(cli, name) if err != nil { fmt.Fprintf(cli.err, "%s\n", err) - encounteredError = fmt.Errorf("Error: failed to wait one or more containers") + errNames = append(errNames, name) } else { fmt.Fprintf(cli.out, "%d\n", status) } } - return encounteredError + if len(errNames) > 0 { + return fmt.Errorf("Error: failed to wait containers: %v", errNames) + } + return nil } diff --git a/integration-cli/docker_cli_rm_test.go b/integration-cli/docker_cli_rm_test.go index ba4e0e6bfd417..f5884dc0fcddd 100644 --- a/integration-cli/docker_cli_rm_test.go +++ b/integration-cli/docker_cli_rm_test.go @@ -105,8 +105,8 @@ func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) { func (s *DockerSuite) TestRmInvalidContainer(c *check.C) { if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "rm", "unknown")); err == nil { c.Fatal("Expected error on rm unknown container, got none") - } else if !strings.Contains(out, "failed to remove one or more containers") { - c.Fatalf("Expected output to contain 'failed to remove one or more containers', got %q", out) + } else if !strings.Contains(out, "failed to remove containers") { + c.Fatalf("Expected output to contain 'failed to remove containers', got %q", out) } } From 101f982059589cac0c8891832ac3f8069291d63a Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Tue, 5 May 2015 19:21:01 +0800 Subject: [PATCH 095/321] Refactor the code of checking conflict option with netmode. Signed-off-by: Lei Jitang --- runconfig/parse.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/runconfig/parse.go b/runconfig/parse.go index 63eeecc5f6ef7..82d0870cd4196 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -15,9 +15,8 @@ import ( var ( ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior.") - ErrConflictContainerNetworkAndDns = fmt.Errorf("Conflicting options: --net=container can't be used with --dns. This configuration is invalid.") + ErrConflictNetworkAndDns = fmt.Errorf("Conflicting options: --dns and the network mode (--net).") ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: -h and the network mode (--net)") - ErrConflictHostNetworkAndDns = fmt.Errorf("Conflicting options: --net=host can't be used with --dns. This configuration is invalid.") ErrConflictHostNetworkAndLinks = fmt.Errorf("Conflicting options: --net=host can't be used with links. This would result in undefined behavior.") ) @@ -112,24 +111,25 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe attachStderr = flAttach.Get("stderr") ) - if *flNetMode != "bridge" && *flNetMode != "none" && *flHostname != "" { + netMode, err := parseNetMode(*flNetMode) + if err != nil { + return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err) + } + + if (netMode.IsHost() || netMode.IsContainer()) && *flHostname != "" { return nil, nil, cmd, ErrConflictNetworkHostname } - if *flNetMode == "host" && flLinks.Len() > 0 { + if netMode.IsHost() && flLinks.Len() > 0 { return nil, nil, cmd, ErrConflictHostNetworkAndLinks } - if strings.HasPrefix(*flNetMode, "container") && flLinks.Len() > 0 { + if netMode.IsContainer() && flLinks.Len() > 0 { return nil, nil, cmd, ErrConflictContainerNetworkAndLinks } - if *flNetMode == "host" && flDns.Len() > 0 { - return nil, nil, cmd, ErrConflictHostNetworkAndDns - } - - if strings.HasPrefix(*flNetMode, "container") && flDns.Len() > 0 { - return nil, nil, cmd, ErrConflictContainerNetworkAndDns + if (netMode.IsHost() || netMode.IsContainer()) && flDns.Len() > 0 { + return nil, nil, cmd, ErrConflictNetworkAndDns } // If neither -d or -a are set, attach to everything by default @@ -266,11 +266,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe return nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode") } - netMode, err := parseNetMode(*flNetMode) - if err != nil { - return nil, nil, cmd, fmt.Errorf("--net: invalid net mode: %v", err) - } - restartPolicy, err := ParseRestartPolicy(*flRestartPolicy) if err != nil { return nil, nil, cmd, err From 0e08e9aca14a4ca7142fa4649983302d93b55dab Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Tue, 5 May 2015 19:27:07 +0800 Subject: [PATCH 096/321] Add support --net=container with --mac-address,--add-host error out Signed-off-by: Lei Jitang --- daemon/container.go | 2 +- docs/sources/reference/run.md | 7 +++++-- integration-cli/docker_cli_run_test.go | 27 ++++++++++++++++++++++++++ runconfig/hostconfig.go | 4 ++++ runconfig/parse.go | 23 ++++++++++++++++------ 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/daemon/container.go b/daemon/container.go index 5c7d3a4e52952..756ad9497b209 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -1107,7 +1107,7 @@ func (container *Container) setupContainerDns() error { return err } - if config.NetworkMode != "host" { + if config.NetworkMode.IsBridge() || config.NetworkMode.IsNone() { // check configurations for any container/daemon dns settings if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { var ( diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 60a180584f63e..43983f3d666e0 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -282,7 +282,8 @@ With the networking mode set to `host` a container will share the host's network stack and all interfaces from the host will be available to the container. The container's hostname will match the hostname on the host system. Publishing ports and linking to other containers will not work -when sharing the host's network stack. +when sharing the host's network stack. Note that `--add-host` `--hostname` +`--dns` `--dns-search` and `--mac-address` is invalid in `host` netmode. Compared to the default `bridge` mode, the `host` mode gives *significantly* better networking performance since it uses the host's native networking stack @@ -298,7 +299,9 @@ or a High Performance Web Server. With the networking mode set to `container` a container will share the network stack of another container. The other container's name must be -provided in the format of `--net container:`. +provided in the format of `--net container:`. Note that `--add-host` +`--hostname` `--dns` `--dns-search` and `--mac-address` is invalid +in `container` netmode. Example running a Redis container with Redis binding to `localhost` then running the `redis-cli` command and connecting to the Redis server over the diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 0cf5c31eeeac9..e828d0c5071dd 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -371,6 +371,33 @@ func (s *DockerSuite) TestRunLinkToContainerNetMode(c *check.C) { } } +func (s *DockerSuite) TestRunContainerNetModeWithDnsMacHosts(c *check.C) { + cmd := exec.Command(dockerBinary, "run", "-d", "--name", "parent", "busybox", "top") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + c.Fatalf("failed to run container: %v, output: %q", err, out) + } + + cmd = exec.Command(dockerBinary, "run", "--dns", "1.2.3.4", "--net=container:parent", "busybox") + out, _, err = runCommandWithOutput(cmd) + if err == nil || !strings.Contains(out, "Conflicting options: --dns and the network mode") { + c.Fatalf("run --net=container with --dns should error out") + } + + cmd = exec.Command(dockerBinary, "run", "--mac-address", "92:d0:c6:0a:29:33", "--net=container:parent", "busybox") + out, _, err = runCommandWithOutput(cmd) + if err == nil || !strings.Contains(out, "--mac-address and the network mode") { + c.Fatalf("run --net=container with --mac-address should error out") + } + + cmd = exec.Command(dockerBinary, "run", "--add-host", "test:192.168.2.109", "--net=container:parent", "busybox") + out, _, err = runCommandWithOutput(cmd) + if err == nil || !strings.Contains(out, "--add-host and the network mode") { + c.Fatalf("run --net=container with --add-host should error out") + } + +} + func (s *DockerSuite) TestRunModeNetContainerHostname(c *check.C) { testRequires(c, ExecSupport) cmd := exec.Command(dockerBinary, "run", "-i", "-d", "--name", "parent", "busybox", "top") diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index d634b1ffb96e5..3a91744af3614 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -21,6 +21,10 @@ func (n NetworkMode) IsPrivate() bool { return !(n.IsHost() || n.IsContainer() || n.IsNone()) } +func (n NetworkMode) IsBridge() bool { + return n == "bridge" +} + func (n NetworkMode) IsHost() bool { return n == "host" } diff --git a/runconfig/parse.go b/runconfig/parse.go index 82d0870cd4196..1fcf521ee32af 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -18,6 +18,8 @@ var ( ErrConflictNetworkAndDns = fmt.Errorf("Conflicting options: --dns and the network mode (--net).") ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: -h and the network mode (--net)") ErrConflictHostNetworkAndLinks = fmt.Errorf("Conflicting options: --net=host can't be used with links. This would result in undefined behavior.") + ErrConflictContainerNetworkAndMac = fmt.Errorf("Conflicting options: --mac-address and the network mode (--net).") + ErrConflictNetworkHosts = fmt.Errorf("Conflicting options: --add-host and the network mode (--net).") ) func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSet, error) { @@ -99,12 +101,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe return nil, nil, cmd, err } - // Validate input params starting with the input mac address - if *flMacAddress != "" { - if _, err := opts.ValidateMACAddress(*flMacAddress); err != nil { - return nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress) - } - } var ( attachStdin = flAttach.Get("stdin") attachStdout = flAttach.Get("stdout") @@ -132,6 +128,21 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe return nil, nil, cmd, ErrConflictNetworkAndDns } + if (netMode.IsContainer() || netMode.IsHost()) && flExtraHosts.Len() > 0 { + return nil, nil, cmd, ErrConflictNetworkHosts + } + + if (netMode.IsContainer() || netMode.IsHost()) && *flMacAddress != "" { + return nil, nil, cmd, ErrConflictContainerNetworkAndMac + } + + // Validate the input mac address + if *flMacAddress != "" { + if _, err := opts.ValidateMACAddress(*flMacAddress); err != nil { + return nil, nil, cmd, fmt.Errorf("%s is not a valid mac address", *flMacAddress) + } + } + // If neither -d or -a are set, attach to everything by default if flAttach.Len() == 0 { attachStdout = true From 3efc083d4a785b2c7b1c1f862133167d5618d216 Mon Sep 17 00:00:00 2001 From: Peter Salvatore Date: Tue, 5 May 2015 11:03:53 -0400 Subject: [PATCH 097/321] Remove blanket "supported" language. Signed-off-by: Peter Salvatore --- docs/sources/docker-hub/official_repos.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/docker-hub/official_repos.md b/docs/sources/docker-hub/official_repos.md index eb73b4bc20117..98c33c6436f71 100644 --- a/docs/sources/docker-hub/official_repos.md +++ b/docs/sources/docker-hub/official_repos.md @@ -5,8 +5,8 @@ page_keywords: Docker, docker, registry, accounts, plans, Dockerfile, Docker Hub # Official Repositories on Docker Hub The Docker [Official Repositories](http://registry.hub.docker.com/official) are -a curated set of Docker repositories that are promoted on Docker Hub and -supported by Docker, Inc. They are designed to: +a curated set of Docker repositories that are promoted on Docker Hub. They are +designed to: * Provide essential base OS repositories (for example, [`ubuntu`](https://registry.hub.docker.com/_/ubuntu/), From 5e563d170815ce3111eb66b44cfd252c80d8f34c Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Tue, 5 May 2015 10:11:59 -0600 Subject: [PATCH 098/321] Replace "docker-core" with "docker-engine" in "build-deb" Signed-off-by: Andrew "Tianon" Page --- hack/make/.build-deb/control | 4 ++-- hack/make/.build-deb/docker-core.install | 8 ++++---- hack/make/.build-deb/rules | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hack/make/.build-deb/control b/hack/make/.build-deb/control index 03caae8342a9e..ac6541a73e058 100644 --- a/hack/make/.build-deb/control +++ b/hack/make/.build-deb/control @@ -1,10 +1,10 @@ -Source: docker-core +Source: docker-engine Maintainer: Docker Homepage: https://dockerproject.com Vcs-Browser: https://github.com/docker/docker Vcs-Git: git://github.com/docker/docker.git -Package: docker-core +Package: docker-engine Architecture: linux-any Depends: iptables, ${misc:Depends}, ${perl:Depends}, ${shlibs:Depends} Recommends: aufs-tools, diff --git a/hack/make/.build-deb/docker-core.install b/hack/make/.build-deb/docker-core.install index c3f4eb146574d..76b05ad043398 100644 --- a/hack/make/.build-deb/docker-core.install +++ b/hack/make/.build-deb/docker-core.install @@ -1,10 +1,10 @@ #contrib/syntax/vim/doc/* /usr/share/vim/vimfiles/doc/ #contrib/syntax/vim/ftdetect/* /usr/share/vim/vimfiles/ftdetect/ #contrib/syntax/vim/syntax/* /usr/share/vim/vimfiles/syntax/ -contrib/*-integration usr/share/docker-core/contrib/ -contrib/check-config.sh usr/share/docker-core/contrib/ +contrib/*-integration usr/share/docker-engine/contrib/ +contrib/check-config.sh usr/share/docker-engine/contrib/ contrib/completion/zsh/_docker usr/share/zsh/vendor-completions/ contrib/init/systemd/docker.service lib/systemd/system/ contrib/init/systemd/docker.socket lib/systemd/system/ -contrib/mk* usr/share/docker-core/contrib/ -contrib/nuke-graph-directory.sh usr/share/docker-core/contrib/ +contrib/mk* usr/share/docker-engine/contrib/ +contrib/nuke-graph-directory.sh usr/share/docker-engine/contrib/ diff --git a/hack/make/.build-deb/rules b/hack/make/.build-deb/rules index 3369f4fc54286..22dab31d12abc 100755 --- a/hack/make/.build-deb/rules +++ b/hack/make/.build-deb/rules @@ -4,7 +4,7 @@ VERSION = $(shell cat VERSION) override_dh_gencontrol: # if we're on Ubuntu, we need to Recommends: apparmor - echo 'apparmor:Recommends=$(shell dpkg-vendor --is Ubuntu && echo apparmor)' >> debian/docker-core.substvars + echo 'apparmor:Recommends=$(shell dpkg-vendor --is Ubuntu && echo apparmor)' >> debian/docker-engine.substvars dh_gencontrol override_dh_auto_build: @@ -19,13 +19,13 @@ override_dh_strip: # also, Go has lots of problems with stripping, so just don't override_dh_auto_install: - mkdir -p debian/docker-core/usr/bin - cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/docker)" debian/docker-core/usr/bin/docker - mkdir -p debian/docker-core/usr/libexec/docker - cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/dockerinit)" debian/docker-core/usr/libexec/docker/dockerinit + mkdir -p debian/docker-engine/usr/bin + cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/docker)" debian/docker-engine/usr/bin/docker + mkdir -p debian/docker-engine/usr/libexec/docker + cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/dockerinit)" debian/docker-engine/usr/libexec/docker/dockerinit override_dh_installinit: - # use "docker" as our service name, not "docker-core" + # use "docker" as our service name, not "docker-engine" dh_installinit --name=docker override_dh_installudev: From 878dcb89f38e8eb7bb07ccd4a4e5ce622252ff30 Mon Sep 17 00:00:00 2001 From: Patrick Devine Date: Tue, 31 Mar 2015 13:58:17 -0700 Subject: [PATCH 099/321] Make a docker-in-docker dynamic binary and add RPM target This change adds a new docker-in-docker dynamic binary make target which builds a centos container for creating the dynamically linked binary. To use it, you first must create the static binary and then call the dind-dynbinary target. You can call it like: $ hack/make.sh binary dind-dynbinary rpm This would then package the dynamic binary into the rpm after having created it in the centos build container. Unfortunately with this approach you can't create the rpms and the debs with the same command. They have to be created separately otherwise the wrong version (static vs. dynamic) gets packaged. Various RPM fixes including: - Adding missing RPM dependencies. - Add sysconfig configuration files to the RPM. - Add an epoch to silence the fpm warning. - Remove unnecessary empty package. Signed-off-by: Patrick Devine Signed-off-by: Chad Metcalf --- Dockerfile | 1 + Dockerfile.centos | 36 ++++++ hack/make.sh | 2 +- hack/make/.integration-daemon-start | 2 +- hack/make/dind-dynbinary | 23 ++++ hack/make/rpm | 193 ++++++++++++++++++++++++++++ 6 files changed, 255 insertions(+), 2 deletions(-) create mode 100644 Dockerfile.centos create mode 100644 hack/make/dind-dynbinary create mode 100644 hack/make/rpm diff --git a/Dockerfile b/Dockerfile index 2b49fc1e0b32c..0c85a863313dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,7 @@ RUN apt-get update && apt-get install -y \ python-pip \ python-websocket \ reprepro \ + rpm \ ruby1.9.1 \ ruby1.9.1-dev \ s3cmd=1.1.0* \ diff --git a/Dockerfile.centos b/Dockerfile.centos new file mode 100644 index 0000000000000..0d11f2f2f7b0e --- /dev/null +++ b/Dockerfile.centos @@ -0,0 +1,36 @@ +# This file creates a CentOS docker container which can be used to create the Docker +# RPMs, however it shouldn't be called directly. +# + +FROM centos:7.0.1406 +MAINTAINER Patrick Devine (@pdev110) + +RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 + +# Packaged dependencies +RUN yum groupinstall -y "Development Tools" + +RUN yum install -y \ + btrfs-progs-devel \ + device-mapper-devel \ + glibc-static \ + libselinux-devel \ + sqlite-devel + +VOLUME /go + +ENV LXC_VERSION 1.0.7 +ENV GO_VERSION 1.4.2 +ENV PATH /go/bin:/usr/local/go/bin:$PATH +ENV GOPATH /go:/go/src/github.com/docker/docker/vendor +ENV GOFMT_VERSION 1.3.3 + +# Add an unprivileged user to be used for tests which need it +RUN groupadd -r docker +RUN useradd --create-home --gid docker unprivilegeduser + +WORKDIR /go/src/github.com/docker/docker +ENV DOCKER_BUILDTAGS selinux btrfs_noversion + +# Wrap all commands in the "docker-in-docker" script to allow nested containers +#ENTRYPOINT ["hack/dind"] diff --git a/hack/make.sh b/hack/make.sh index 699bedb3795a3..96ca5902bc122 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -274,7 +274,7 @@ main() { # We want this to fail if the bundles already exist and cannot be removed. # This is to avoid mixing bundles from different versions of the code. mkdir -p bundles - if [ -e "bundles/$VERSION" ]; then + if [ -e "bundles/$VERSION" ] && [ -z ${KEEPBUNDLE} ]; then echo "bundles/$VERSION already exists. Removing." rm -fr "bundles/$VERSION" && mkdir "bundles/$VERSION" || exit 1 echo diff --git a/hack/make/.integration-daemon-start b/hack/make/.integration-daemon-start index 57fd525028e74..0a889ae7a2a97 100644 --- a/hack/make/.integration-daemon-start +++ b/hack/make/.integration-daemon-start @@ -2,7 +2,7 @@ # see test-integration-cli for example usage of this script -export PATH="$DEST/../binary:$DEST/../dynbinary:$DEST/../gccgo:$DEST/../dyngccgo:$PATH" +export PATH="$DEST/../dynbinary:$DEST/../binary:$DEST/../gccgo:$DEST/../dyngccgo:$PATH" if ! command -v docker &> /dev/null; then echo >&2 'error: binary or dynbinary must be run before .integration-daemon-start' diff --git a/hack/make/dind-dynbinary b/hack/make/dind-dynbinary new file mode 100644 index 0000000000000..a577ca0763cde --- /dev/null +++ b/hack/make/dind-dynbinary @@ -0,0 +1,23 @@ +#!/bin/bash + +DEST=$1 +DOCKERBIN=$DEST/../binary/docker +BUILDDIR=$DEST/../dynbinary/ +RPM_PATH=$DEST/../rpm/ + +build_rpm() { + if [ ! -x $DOCKERBIN ]; then + echo "No docker binary was found to execute. This step requires 'binary' to be run first." + exit 1 + fi + + $DOCKERBIN -d 2>/dev/null & + $DOCKERBIN build -t centos-build -f Dockerfile.centos ./ + $DOCKERBIN run -it --rm --privileged -v /go:/go -v /usr/local:/usr/local -e "KEEPBUNDLE=true" --name centos-build-container centos-build hack/make.sh dynbinary + + # turn off the docker daemon + $DOCKERBIN rmi centos-build + cat /var/run/docker.pid | xargs kill +} + +build_rpm diff --git a/hack/make/rpm b/hack/make/rpm new file mode 100644 index 0000000000000..6340f79131f07 --- /dev/null +++ b/hack/make/rpm @@ -0,0 +1,193 @@ +#!/bin/bash + +DEST=$1 +PACKAGE_NAME=${PACKAGE_NAME:-docker-engine} + +# XXX - The package version in CentOS gets messed up and inserts a '~' +# (including the single quote) if we use the same package +# version scheme as the deb packages. This doesn't work with +# rpmbuild. +PKGVERSION="${VERSION}" +# if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better +if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then + GIT_UNIX="$(git log -1 --pretty='%at')" + GIT_DATE="$(date --date "@$GIT_UNIX" +'%Y%m%d.%H%M%S')" + GIT_COMMIT="$(git log -1 --pretty='%h')" + GIT_VERSION="git${GIT_DATE}.0.${GIT_COMMIT}" + # GIT_VERSION is now something like 'git20150128.112847.0.17e840a' + PKGVERSION="$PKGVERSION~$GIT_VERSION" +fi + +# $ dpkg --compare-versions 1.5.0 gt 1.5.0~rc1 && echo true || echo false +# true +# $ dpkg --compare-versions 1.5.0~rc1 gt 1.5.0~git20150128.112847.17e840a && echo true || echo false +# true +# $ dpkg --compare-versions 1.5.0~git20150128.112847.17e840a gt 1.5.0~dev~git20150128.112847.17e840a && echo true || echo false +# true + +# ie, 1.5.0 > 1.5.0~rc1 > 1.5.0~git20150128.112847.17e840a > 1.5.0~dev~git20150128.112847.17e840a + +PACKAGE_ARCHITECTURE=`uname -i` + +PACKAGE_URL="http://www.docker.com/" +PACKAGE_MAINTAINER="support@docker.com" +PACKAGE_DESCRIPTION="Linux container runtime +Docker complements LXC with a high-level API which operates at the process +level. It runs unix processes with strong guarantees of isolation and +repeatability across servers. +Docker is a great building block for automating distributed systems: +large-scale web deployments, database clusters, continuous deployment systems, +private PaaS, service-oriented architectures, etc." +PACKAGE_LICENSE="Apache-2.0" + +# bundle the RPM using FPM -- we may want to change this to rpmbuild at some point +bundle_rpm() { + DIR=$DEST/build + + # Include our udev rules + mkdir -p $DIR/etc/udev/rules.d + cp contrib/udev/80-docker.rules $DIR/etc/udev/rules.d/ + + mkdir -p $DIR/usr/lib/systemd/system + cp contrib/init/systemd/docker.{service,socket} $DIR/usr/lib/systemd/system + + cat > $DIR/usr/lib/systemd/system/docker.service <<'EOF' +[Unit] +Description=Docker Application Container Engine +Documentation=http://docs.docker.com +After=network.target docker.socket +Requires=docker.socket + +[Service] +Type=notify +EnvironmentFile=-/etc/sysconfig/docker +EnvironmentFile=-/etc/sysconfig/docker-storage +ExecStart=/usr/bin/docker -d $OPTIONS $DOCKER_STORAGE_OPTIONS +LimitNOFILE=1048576 +LimitNPROC=1048576 +MountFlags=private + +[Install] +WantedBy=multi-user.target +EOF + + mkdir -p $DIR/etc/sysconfig + cat > $DIR/etc/sysconfig/docker <<'EOF' +# /etc/sysconfig/docker + +# Modify these options if you want to change the way the docker daemon runs +OPTIONS=--selinux-enabled -H fd:// + +# Location used for temporary files, such as those created by +# docker load and build operations. Default is /var/lib/docker/tmp +# Can be overriden by setting the following environment variable. +# DOCKER_TMPDIR=/var/tmp +EOF + +cat > $DIR/etc/sysconfig/docker-storage <<'EOF' +# By default, Docker uses a loopback-mounted sparse file in +# /var/lib/docker. The loopback makes it slower, and there are some +# restrictive defaults, such as 100GB max storage. + +# If your installation did not set a custom storage for Docker, you +# may do it below. + +# Example: Use a custom pair of raw logical volumes (one for metadata, +# one for data). +# DOCKER_STORAGE_OPTIONS = --storage-opt dm.metadatadev=/dev/mylogvol/my-docker-metadata --storage-opt dm.datadev=/dev/mylogvol/my-docker-data + +DOCKER_STORAGE_OPTIONS= +EOF + + # Include contributed completions + mkdir -p $DIR/etc/bash_completion.d + cp contrib/completion/bash/docker $DIR/etc/bash_completion.d/ + mkdir -p $DIR/usr/share/zsh/vendor-completions + cp contrib/completion/zsh/_docker $DIR/usr/share/zsh/vendor-completions/ + mkdir -p $DIR/etc/fish/completions + cp contrib/completion/fish/docker.fish $DIR/etc/fish/completions/ + + # Include contributed man pages + docs/man/md2man-all.sh -q + manRoot="$DIR/usr/share/man" + mkdir -p "$manRoot" + for manDir in docs/man/man?; do + manBase="$(basename "$manDir")" # "man1" + for manFile in "$manDir"/*; do + manName="$(basename "$manFile")" # "docker-build.1" + mkdir -p "$manRoot/$manBase" + gzip -c "$manFile" > "$manRoot/$manBase/$manName.gz" + done + done + + # Copy the binary + # This will fail if the dynbinary bundle hasn't been built + mkdir -p $DIR/usr/bin + cp $DEST/../dynbinary/docker-$VERSION $DIR/usr/bin/docker + cp $DEST/../dynbinary/dockerinit-$VERSION $DIR/usr/bin/dockerinit + + # Generate postinst/prerm/postrm scripts + cat > $DEST/postinst <<'EOF' +EOF + + cat > $DEST/preinst <<'EOF' +if ! getent group docker > /dev/null; then + groupadd --system docker +fi +EOF + + cat > $DEST/prerm <<'EOF' +EOF + + cat > $DEST/postrm <<'EOF' +## In case this system is running systemd, we make systemd reload the unit files +## to pick up changes. +#if [ -d /run/systemd/system ] ; then +# systemctl --system daemon-reload > /dev/null || true +#fi +EOF + + chmod +x $DEST/postinst $DEST/prerm $DEST/postrm $DEST/preinst + + ( + # switch directories so we create *.deb in the right folder + cd $DEST + + # create PACKAGE_NAME-VERSION package + fpm -s dir -C $DIR \ + --name $PACKAGE_NAME-$VERSION --version "$PKGVERSION" \ + --epoch 7 \ + --before-install $DEST/preinst \ + --after-install $DEST/postinst \ + --before-remove $DEST/prerm \ + --after-remove $DEST/postrm \ + --architecture "$PACKAGE_ARCHITECTURE" \ + --prefix / \ + --depends iptables \ + --depends xz \ + --depends "systemd >= 208-20" \ + --depends "device-mapper-libs >= 7:1.02.90-1" \ + --depends "device-mapper-event-libs >= 7:1.02.90-1" \ + --depends libselinux \ + --depends libsepol \ + --depends sqlite \ + --description "$PACKAGE_DESCRIPTION" \ + --maintainer "$PACKAGE_MAINTAINER" \ + --conflicts docker \ + --conflicts docker-io \ + --conflicts lxc-docker-virtual-package \ + --conflicts lxc-docker \ + --url "$PACKAGE_URL" \ + --license "$PACKAGE_LICENSE" \ + --config-files etc/sysconfig \ + --config-files etc/udev/rules.d/80-docker.rules \ + --rpm-compression gzip \ + -t rpm . + ) + + # clean up after ourselves so we have a clean output directory + rm $DEST/postinst $DEST/prerm $DEST/postrm $DEST/preinst + rm -r $DIR +} + +bundle_rpm From 8c9d67921a44e4f189373d8dc10fc8946039918d Mon Sep 17 00:00:00 2001 From: Mary Anthony Date: Tue, 5 May 2015 14:33:31 -0700 Subject: [PATCH 100/321] Updating branch for machine docs Signed-off-by: Mary Anthony --- docs/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Dockerfile b/docs/Dockerfile index a53048bb7f8b7..8ece2b09eeead 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -8,7 +8,7 @@ MAINTAINER Sven Dowideit (@SvenDowideit) # sub project ENV COMPOSE_BRANCH release ENV SWARM_BRANCH v0.2.0 -ENV MACHINE_BRANCH master +ENV MACHINE_BRANCH docs ENV DISTRIB_BRANCH docs From 08b7f30fcd050244026098673b19700485308b5a Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Tue, 5 May 2015 14:27:42 -0700 Subject: [PATCH 101/321] Fix issue where build steps are duplicated in the output This fixes an issue where the build output for the "Steps" would look like: ``` Step 1: RUN echo hi echo hi ``` instead of ``` Step 1: RUN echo hi ``` Also, I noticed that there were no checks to make sure invalid Dockerfile cmd flags were caught on cmds that didn't use cmd flags at all. They would have been caught on the cmds that had flags, but cmds that didn't bother to add a new code for flags would have just ignored them. So, I added checks to each cmd to flag it. Added testcases for issues. Signed-off-by: Doug Davis --- builder/dispatchers.go | 56 ++++++++++++++++++++++++ builder/evaluator.go | 11 ++++- integration-cli/docker_cli_build_test.go | 35 +++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/builder/dispatchers.go b/builder/dispatchers.go index 195d18305d03c..7d1dab640742a 100644 --- a/builder/dispatchers.go +++ b/builder/dispatchers.go @@ -47,6 +47,10 @@ func env(b *Builder, args []string, attributes map[string]bool, original string) return fmt.Errorf("Bad input to ENV, too many args") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + // TODO/FIXME/NOT USED // Just here to show how to use the builder flags stuff within the // context of a builder command. Will remove once we actually add @@ -97,6 +101,10 @@ func maintainer(b *Builder, args []string, attributes map[string]bool, original return fmt.Errorf("MAINTAINER requires exactly one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + b.maintainer = args[0] return b.commit("", b.Config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer)) } @@ -114,6 +122,10 @@ func label(b *Builder, args []string, attributes map[string]bool, original strin return fmt.Errorf("Bad input to LABEL, too many args") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + commitStr := "LABEL" if b.Config.Labels == nil { @@ -142,6 +154,10 @@ func add(b *Builder, args []string, attributes map[string]bool, original string) return fmt.Errorf("ADD requires at least two arguments") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + return b.runContextCommand(args, true, true, "ADD") } @@ -154,6 +170,10 @@ func dispatchCopy(b *Builder, args []string, attributes map[string]bool, origina return fmt.Errorf("COPY requires at least two arguments") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + return b.runContextCommand(args, false, false, "COPY") } @@ -166,6 +186,10 @@ func from(b *Builder, args []string, attributes map[string]bool, original string return fmt.Errorf("FROM requires one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + name := args[0] if name == NoBaseImageSpecifier { @@ -210,6 +234,10 @@ func onbuild(b *Builder, args []string, attributes map[string]bool, original str return fmt.Errorf("ONBUILD requires at least one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0])) switch triggerInstruction { case "ONBUILD": @@ -233,6 +261,10 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str return fmt.Errorf("WORKDIR requires exactly one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + workdir := args[0] if !filepath.IsAbs(workdir) { @@ -258,6 +290,10 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) return fmt.Errorf("Please provide a source image with `from` prior to run") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + args = handleJsonArgs(args, attributes) if !attributes["json"] { @@ -317,6 +353,10 @@ func run(b *Builder, args []string, attributes map[string]bool, original string) // Argument handling is the same as RUN. // func cmd(b *Builder, args []string, attributes map[string]bool, original string) error { + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + cmdSlice := handleJsonArgs(args, attributes) if !attributes["json"] { @@ -345,6 +385,10 @@ func cmd(b *Builder, args []string, attributes map[string]bool, original string) // is initialized at NewBuilder time instead of through argument parsing. // func entrypoint(b *Builder, args []string, attributes map[string]bool, original string) error { + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + parsed := handleJsonArgs(args, attributes) switch { @@ -384,6 +428,10 @@ func expose(b *Builder, args []string, attributes map[string]bool, original stri return fmt.Errorf("EXPOSE requires at least one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + if b.Config.ExposedPorts == nil { b.Config.ExposedPorts = make(nat.PortSet) } @@ -428,6 +476,10 @@ func user(b *Builder, args []string, attributes map[string]bool, original string return fmt.Errorf("USER requires exactly one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + b.Config.User = args[0] return b.commit("", b.Config.Cmd, fmt.Sprintf("USER %v", args)) } @@ -441,6 +493,10 @@ func volume(b *Builder, args []string, attributes map[string]bool, original stri return fmt.Errorf("VOLUME requires at least one argument") } + if err := b.BuilderFlags.Parse(); err != nil { + return err + } + if b.Config.Volumes == nil { b.Config.Volumes = map[string]struct{}{} } diff --git a/builder/evaluator.go b/builder/evaluator.go index cd8bff1ceccdb..bdcc6b29a853f 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -280,7 +280,11 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error { original := ast.Original flags := ast.Flags strs := []string{} - msg := fmt.Sprintf("Step %d : %s", stepN, original) + msg := fmt.Sprintf("Step %d : %s", stepN, strings.ToUpper(cmd)) + + if len(ast.Flags) > 0 { + msg += " " + strings.Join(ast.Flags, " ") + } if cmd == "onbuild" { if ast.Next == nil { @@ -289,6 +293,11 @@ func (b *Builder) dispatch(stepN int, ast *parser.Node) error { ast = ast.Next.Children[0] strs = append(strs, ast.Value) msg += " " + ast.Value + + if len(ast.Flags) > 0 { + msg += " " + strings.Join(ast.Flags, " ") + } + } // count the number of nodes that we are going to traverse first diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 20a317246e326..5247a11ec5996 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5349,3 +5349,38 @@ RUN cat /proc/self/cgroup c.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err) } } + +func (s *DockerSuite) TestBuildNoDupOutput(c *check.C) { + // Check to make sure our build output prints the Dockerfile cmd + // property - there was a bug that caused it to be duplicated on the + // Step X line + name := "testbuildnodupoutput" + + _, out, err := buildImageWithOut(name, ` + FROM busybox + RUN env`, false) + if err != nil { + c.Fatalf("Build should have worked: %q", err) + } + + exp := "\nStep 1 : RUN env\n" + if !strings.Contains(out, exp) { + c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", out, exp) + } +} + +func (s *DockerSuite) TestBuildBadCmdFlag(c *check.C) { + name := "testbuildbadcmdflag" + + _, out, err := buildImageWithOut(name, ` + FROM busybox + MAINTAINER --boo joe@example.com`, false) + if err == nil { + c.Fatal("Build should have failed") + } + + exp := `"Unknown flag: boo"` + if !strings.Contains(out, exp) { + c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", out, exp) + } +} From 18beb5561140aaa950f00391a87bb332fb2b6aea Mon Sep 17 00:00:00 2001 From: Jessica Frazelle Date: Thu, 30 Apr 2015 15:30:42 -0700 Subject: [PATCH 102/321] Add rpm for centos-6, centos-7, fedora-20, fedora-21 Signed-off-by: Jessica Frazelle --- Dockerfile | 1 - Dockerfile.centos | 36 ----- contrib/builder/rpm/README.md | 5 + contrib/builder/rpm/build.sh | 10 ++ contrib/builder/rpm/centos-6/Dockerfile | 15 ++ contrib/builder/rpm/centos-7/Dockerfile | 15 ++ contrib/builder/rpm/fedora-20/Dockerfile | 15 ++ contrib/builder/rpm/fedora-21/Dockerfile | 15 ++ contrib/builder/rpm/generate.sh | 73 +++++++++ hack/make.sh | 2 +- hack/make/.build-rpm/docker-engine.spec | 180 +++++++++++++++++++++ hack/make/.integration-daemon-start | 2 +- hack/make/build-rpm | 73 +++++++++ hack/make/dind-dynbinary | 23 --- hack/make/rpm | 193 ----------------------- 15 files changed, 403 insertions(+), 255 deletions(-) delete mode 100644 Dockerfile.centos create mode 100644 contrib/builder/rpm/README.md create mode 100755 contrib/builder/rpm/build.sh create mode 100644 contrib/builder/rpm/centos-6/Dockerfile create mode 100644 contrib/builder/rpm/centos-7/Dockerfile create mode 100644 contrib/builder/rpm/fedora-20/Dockerfile create mode 100644 contrib/builder/rpm/fedora-21/Dockerfile create mode 100755 contrib/builder/rpm/generate.sh create mode 100644 hack/make/.build-rpm/docker-engine.spec create mode 100644 hack/make/build-rpm delete mode 100644 hack/make/dind-dynbinary delete mode 100644 hack/make/rpm diff --git a/Dockerfile b/Dockerfile index 0c85a863313dd..2b49fc1e0b32c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,7 +47,6 @@ RUN apt-get update && apt-get install -y \ python-pip \ python-websocket \ reprepro \ - rpm \ ruby1.9.1 \ ruby1.9.1-dev \ s3cmd=1.1.0* \ diff --git a/Dockerfile.centos b/Dockerfile.centos deleted file mode 100644 index 0d11f2f2f7b0e..0000000000000 --- a/Dockerfile.centos +++ /dev/null @@ -1,36 +0,0 @@ -# This file creates a CentOS docker container which can be used to create the Docker -# RPMs, however it shouldn't be called directly. -# - -FROM centos:7.0.1406 -MAINTAINER Patrick Devine (@pdev110) - -RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 - -# Packaged dependencies -RUN yum groupinstall -y "Development Tools" - -RUN yum install -y \ - btrfs-progs-devel \ - device-mapper-devel \ - glibc-static \ - libselinux-devel \ - sqlite-devel - -VOLUME /go - -ENV LXC_VERSION 1.0.7 -ENV GO_VERSION 1.4.2 -ENV PATH /go/bin:/usr/local/go/bin:$PATH -ENV GOPATH /go:/go/src/github.com/docker/docker/vendor -ENV GOFMT_VERSION 1.3.3 - -# Add an unprivileged user to be used for tests which need it -RUN groupadd -r docker -RUN useradd --create-home --gid docker unprivilegeduser - -WORKDIR /go/src/github.com/docker/docker -ENV DOCKER_BUILDTAGS selinux btrfs_noversion - -# Wrap all commands in the "docker-in-docker" script to allow nested containers -#ENTRYPOINT ["hack/dind"] diff --git a/contrib/builder/rpm/README.md b/contrib/builder/rpm/README.md new file mode 100644 index 0000000000000..153fbceb6a8b0 --- /dev/null +++ b/contrib/builder/rpm/README.md @@ -0,0 +1,5 @@ +# `dockercore/builder-rpm` + +This image's tags contain the dependencies for building Docker `.rpm`s for each of the RPM-based platforms Docker targets. + +To add new tags, see [`contrib/builder/rpm` in https://github.com/docker/docker](https://github.com/docker/docker/tree/master/contrib/builder/rpm), specifically the `generate.sh` script, whose usage is described in a comment at the top of the file. diff --git a/contrib/builder/rpm/build.sh b/contrib/builder/rpm/build.sh new file mode 100755 index 0000000000000..558f7ee0dbafe --- /dev/null +++ b/contrib/builder/rpm/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +set -x +./generate.sh +for d in */; do + docker build -t "dockercore/builder-rpm:$(basename "$d")" "$d" +done diff --git a/contrib/builder/rpm/centos-6/Dockerfile b/contrib/builder/rpm/centos-6/Dockerfile new file mode 100644 index 0000000000000..d248142270d42 --- /dev/null +++ b/contrib/builder/rpm/centos-6/Dockerfile @@ -0,0 +1,15 @@ +# +# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"! +# + +FROM centos:6 + +RUN yum groupinstall -y "Development Tools" +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar + +ENV GO_VERSION 1.4.2 +RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xvzC /usr/local +ENV PATH $PATH:/usr/local/go/bin + +ENV AUTO_GOPATH 1 +ENV DOCKER_BUILDTAGS selinux exclude_graphdriver_btrfs diff --git a/contrib/builder/rpm/centos-7/Dockerfile b/contrib/builder/rpm/centos-7/Dockerfile new file mode 100644 index 0000000000000..a58c9f58bd720 --- /dev/null +++ b/contrib/builder/rpm/centos-7/Dockerfile @@ -0,0 +1,15 @@ +# +# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"! +# + +FROM centos:7 + +RUN yum groupinstall -y "Development Tools" +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar + +ENV GO_VERSION 1.4.2 +RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xvzC /usr/local +ENV PATH $PATH:/usr/local/go/bin + +ENV AUTO_GOPATH 1 +ENV DOCKER_BUILDTAGS selinux diff --git a/contrib/builder/rpm/fedora-20/Dockerfile b/contrib/builder/rpm/fedora-20/Dockerfile new file mode 100644 index 0000000000000..f5642cdf0369c --- /dev/null +++ b/contrib/builder/rpm/fedora-20/Dockerfile @@ -0,0 +1,15 @@ +# +# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"! +# + +FROM fedora:20 + +RUN yum install -y @development-tools fedora-packager +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar + +ENV GO_VERSION 1.4.2 +RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xvzC /usr/local +ENV PATH $PATH:/usr/local/go/bin + +ENV AUTO_GOPATH 1 +ENV DOCKER_BUILDTAGS selinux diff --git a/contrib/builder/rpm/fedora-21/Dockerfile b/contrib/builder/rpm/fedora-21/Dockerfile new file mode 100644 index 0000000000000..18e7837c75580 --- /dev/null +++ b/contrib/builder/rpm/fedora-21/Dockerfile @@ -0,0 +1,15 @@ +# +# THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"! +# + +FROM fedora:21 + +RUN yum install -y @development-tools fedora-packager +RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libselinux-devel sqlite-devel tar + +ENV GO_VERSION 1.4.2 +RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xvzC /usr/local +ENV PATH $PATH:/usr/local/go/bin + +ENV AUTO_GOPATH 1 +ENV DOCKER_BUILDTAGS selinux diff --git a/contrib/builder/rpm/generate.sh b/contrib/builder/rpm/generate.sh new file mode 100755 index 0000000000000..7bfd06f678c40 --- /dev/null +++ b/contrib/builder/rpm/generate.sh @@ -0,0 +1,73 @@ +#!/bin/bash +set -e + +# usage: ./generate.sh [versions] +# ie: ./generate.sh +# to update all Dockerfiles in this directory +# or: ./generate.sh +# to only update fedora-20/Dockerfile +# or: ./generate.sh fedora-newversion +# to create a new folder and a Dockerfile within it + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +versions=( "$@" ) +if [ ${#versions[@]} -eq 0 ]; then + versions=( */ ) +fi +versions=( "${versions[@]%/}" ) + +for version in "${versions[@]}"; do + distro="${version%-*}" + suite="${version##*-}" + from="${distro}:${suite}" + + mkdir -p "$version" + echo "$version -> FROM $from" + cat > "$version/Dockerfile" <<-EOF + # + # THIS FILE IS AUTOGENERATED; SEE "contrib/builder/rpm/generate.sh"! + # + + FROM $from + EOF + + echo >> "$version/Dockerfile" + + case "$from" in + centos:*) + # get "Development Tools" packages dependencies + echo 'RUN yum groupinstall -y "Development Tools"' >> "$version/Dockerfile" + ;; + *) + echo 'RUN yum install -y @development-tools fedora-packager' >> "$version/Dockerfile" + ;; + esac + + # this list is sorted alphabetically; please keep it that way + packages=( + btrfs-progs-devel # for "btrfs/ioctl.h" (and "version.h" if possible) + device-mapper-devel # for "libdevmapper.h" + glibc-static + libselinux-devel # for "libselinux.so" + sqlite-devel # for "sqlite3.h" + tar # older versions of dev-tools don't have tar + ) + echo "RUN yum install -y ${packages[*]}" >> "$version/Dockerfile" + + echo >> "$version/Dockerfile" + + awk '$1 == "ENV" && $2 == "GO_VERSION" { print; exit }' ../../../Dockerfile >> "$version/Dockerfile" + echo 'RUN curl -fsSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz" | tar xvzC /usr/local' >> "$version/Dockerfile" + echo 'ENV PATH $PATH:/usr/local/go/bin' >> "$version/Dockerfile" + + echo >> "$version/Dockerfile" + + echo 'ENV AUTO_GOPATH 1' >> "$version/Dockerfile" + + if [ "$from" == "centos:6" ]; then + echo 'ENV DOCKER_BUILDTAGS selinux exclude_graphdriver_btrfs' >> "$version/Dockerfile" + else + echo 'ENV DOCKER_BUILDTAGS selinux' >> "$version/Dockerfile" + fi +done diff --git a/hack/make.sh b/hack/make.sh index 96ca5902bc122..699bedb3795a3 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -274,7 +274,7 @@ main() { # We want this to fail if the bundles already exist and cannot be removed. # This is to avoid mixing bundles from different versions of the code. mkdir -p bundles - if [ -e "bundles/$VERSION" ] && [ -z ${KEEPBUNDLE} ]; then + if [ -e "bundles/$VERSION" ]; then echo "bundles/$VERSION already exists. Removing." rm -fr "bundles/$VERSION" && mkdir "bundles/$VERSION" || exit 1 echo diff --git a/hack/make/.build-rpm/docker-engine.spec b/hack/make/.build-rpm/docker-engine.spec new file mode 100644 index 0000000000000..1bb654df80a84 --- /dev/null +++ b/hack/make/.build-rpm/docker-engine.spec @@ -0,0 +1,180 @@ +Name: docker-engine +Version: %{_version} +Release: %{_release}%{?dist} +Summary: The open-source application container engine + +License: ASL 2.0 +Source: %{name}.tar.gz + +URL: https://dockerproject.com +Vendor: Docker +Packager: Docker + +# docker builds in a checksum of dockerinit into docker, +# # so stripping the binaries breaks docker +%global __os_install_post %{_rpmconfigdir}/brp-compress +%global debug_package %{nil} + +# is_systemd conditional +%if 0%{?fedora} >= 21 || 0%{?centos} >= 7 || 0%{?rhel} >= 7 +%global is_systemd 1 +%endif + +# required packages for build +# most are already in the container (see contrib/builder/rpm/generate.sh) +# only require systemd on those systems +%if 0%{?is_systemd} +BuildRequires: pkgconfig(systemd) +Requires: systemd-units +%else +Requires(post): chkconfig +Requires(preun): chkconfig +# This is for /sbin/service +Requires(preun): initscripts +%endif + +# 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 +# Resolves: rhbz#1165615 +Requires: device-mapper-libs >= 1.02.90-1 +%endif + +# conflicting packages +Conflicts: docker +Conflicts: docker-io + +%description +Docker is an open source project to pack, ship and run any application as a +lightweight container + +Docker containers are both hardware-agnostic and platform-agnostic. This means +they can run anywhere, from your laptop to the largest EC2 compute instance and +everything in between - and they don't require you to use a particular +language, framework or packaging system. That makes them great building blocks +for deploying and scaling web apps, databases, and backend services without +depending on a particular stack or provider. + +%prep +%if 0%{?centos} <= 6 +%setup -n %{name} +%else +%autosetup -n %{name} +%endif + +%build +./hack/make.sh dynbinary +# ./docs/man/md2man-all.sh runs outside the build container (if at all), since we don't have go-md2man here + +%check +./bundles/%{_origversion}/dynbinary/docker -v + +%install +# install binary +install -d $RPM_BUILD_ROOT/%{_bindir} +install -p -m 755 bundles/%{_origversion}/dynbinary/docker-%{_origversion} $RPM_BUILD_ROOT/%{_bindir}/docker + +# install dockerinit +install -d $RPM_BUILD_ROOT/%{_libexecdir}/docker +install -p -m 755 bundles/%{_origversion}/dynbinary/dockerinit-%{_origversion} $RPM_BUILD_ROOT/%{_libexecdir}/docker/dockerinit + +# install udev rules +install -d $RPM_BUILD_ROOT/%{_sysconfdir}/udev/rules.d +install -p -m 755 contrib/udev/80-docker.rules $RPM_BUILD_ROOT/%{_sysconfdir}/udev/rules.d/80-docker.rules + +# add init scripts +install -d $RPM_BUILD_ROOT/etc/sysconfig +install -d $RPM_BUILD_ROOT/%{_initddir} + + +%if 0%{?is_systemd} +install -d $RPM_BUILD_ROOT/%{_unitdir} +install -p -m 644 contrib/init/systemd/docker.service $RPM_BUILD_ROOT/%{_unitdir}/docker.service +install -p -m 644 contrib/init/systemd/docker.socket $RPM_BUILD_ROOT/%{_unitdir}/docker.socket +%endif + +install -p -m 644 contrib/init/sysvinit-redhat/docker.sysconfig $RPM_BUILD_ROOT/etc/sysconfig/docker +install -p -m 755 contrib/init/sysvinit-redhat/docker $RPM_BUILD_ROOT/%{_initddir}/docker + +# add bash completions +install -d $RPM_BUILD_ROOT/usr/share/bash-completion/completions +install -d $RPM_BUILD_ROOT/usr/share/zsh/vendor-completions +install -d $RPM_BUILD_ROOT/usr/share/fish/completions +install -p -m 644 contrib/completion/bash/docker $RPM_BUILD_ROOT/usr/share/bash-completion/completions/docker +install -p -m 644 contrib/completion/zsh/_docker $RPM_BUILD_ROOT/usr/share/zsh/vendor-completions/_docker +install -p -m 644 contrib/completion/fish/docker.fish $RPM_BUILD_ROOT/usr/share/fish/completions/docker.fish + +# install manpages +install -d %{buildroot}%{_mandir}/man1 +install -p -m 644 docs/man/man1/*.1 $RPM_BUILD_ROOT/%{_mandir}/man1 +install -d %{buildroot}%{_mandir}/man5 +install -p -m 644 docs/man/man5/*.5 $RPM_BUILD_ROOT/%{_mandir}/man5 + +# add vimfiles +install -d $RPM_BUILD_ROOT/usr/share/vim/vimfiles/doc +install -d $RPM_BUILD_ROOT/usr/share/vim/vimfiles/ftdetect +install -d $RPM_BUILD_ROOT/usr/share/vim/vimfiles/syntax +install -p -m 644 contrib/syntax/vim/doc/dockerfile.txt $RPM_BUILD_ROOT/usr/share/vim/vimfiles/doc/dockerfile.txt +install -p -m 644 contrib/syntax/vim/ftdetect/dockerfile.vim $RPM_BUILD_ROOT/usr/share/vim/vimfiles/ftdetect/dockerfile.vim +install -p -m 644 contrib/syntax/vim/syntax/dockerfile.vim $RPM_BUILD_ROOT/usr/share/vim/vimfiles/syntax/dockerfile.vim + + +# list files owned by the package here +%files +/%{_bindir}/docker +/%{_libexecdir}/docker/dockerinit +/%{_sysconfdir}/udev/rules.d/80-docker.rules +%if 0%{?is_systemd} +/%{_unitdir}/docker.service +/%{_unitdir}/docker.socket +%endif +/etc/sysconfig/docker +/%{_initddir}/docker +/usr/share/bash-completion/completions/docker +/usr/share/zsh/vendor-completions/_docker +/usr/share/fish/completions/docker.fish +%doc +/%{_mandir}/man1/* +/%{_mandir}/man5/* +/usr/share/vim/vimfiles/doc/dockerfile.txt +/usr/share/vim/vimfiles/ftdetect/dockerfile.vim +/usr/share/vim/vimfiles/syntax/dockerfile.vim + +%post +%if 0%{?is_systemd} +%systemd_post docker +%else +# This adds the proper /etc/rc*.d links for the script +/sbin/chkconfig --add docker +%endif +if ! getent group docker > /dev/null; then + groupadd --system docker +fi + +%preun +%if 0%{?is_systemd} +%systemd_preun docker +%else +if [ $1 -eq 0 ] ; then + /sbin/service docker stop >/dev/null 2>&1 + /sbin/chkconfig --del docker +fi +%endif + +%postun +%if 0%{?is_systemd} +%systemd_postun_with_restart docker +%else +if [ "$1" -ge "1" ] ; then + /sbin/service docker condrestart >/dev/null 2>&1 || : +fi +%endif + +%changelog diff --git a/hack/make/.integration-daemon-start b/hack/make/.integration-daemon-start index 0a889ae7a2a97..57fd525028e74 100644 --- a/hack/make/.integration-daemon-start +++ b/hack/make/.integration-daemon-start @@ -2,7 +2,7 @@ # see test-integration-cli for example usage of this script -export PATH="$DEST/../dynbinary:$DEST/../binary:$DEST/../gccgo:$DEST/../dyngccgo:$PATH" +export PATH="$DEST/../binary:$DEST/../dynbinary:$DEST/../gccgo:$DEST/../dyngccgo:$PATH" if ! command -v docker &> /dev/null; then echo >&2 'error: binary or dynbinary must be run before .integration-daemon-start' diff --git a/hack/make/build-rpm b/hack/make/build-rpm new file mode 100644 index 0000000000000..0f3ff6d00e73b --- /dev/null +++ b/hack/make/build-rpm @@ -0,0 +1,73 @@ +#!/bin/bash +set -e + +DEST=$1 + +# subshell so that we can export PATH without breaking other things +( + source "$(dirname "$BASH_SOURCE")/.integration-daemon-start" + + # TODO consider using frozen images for the dockercore/builder-rpm tags + + rpmName=docker-engine + rpmVersion="${VERSION%%-*}" + rpmRelease=1 + + # rpmRelease versioning is as follows + # Docker 1.7.0: version=1.7.0, release=1 + # Docker 1.7.0-rc1: version=1.7.0, release=0.1.rc1 + # Docker 1.7.0-dev nightly: version=1.7.0, release=0.0.YYYYMMDD.HHMMSS.gitHASH + + # if we have a "-rc*" suffix, set appropriate release + if [[ "$VERSION" == *-rc* ]]; then + rcVersion=${VERSION#*-rc} + rpmRelease="0.${rcVersion}.rc${rcVersion}" + fi + + # if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better + if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then + gitUnix="$(git log -1 --pretty='%at')" + gitDate="$(date --date "@$gitUnix" +'%Y%m%d.%H%M%S')" + gitCommit="$(git log -1 --pretty='%h')" + gitVersion="${gitDate}.git${gitCommit}" + # gitVersion is now something like '20150128.112847.17e840a' + rpmRelease="0.0.$gitVersion" + fi + + rpmPackager="$(awk -F ': ' '$1 == "Packager" { print $2; exit }' hack/make/.build-rpm/${rpmName}.spec)" + rpmDate="$(date +'%a %b %d %Y')" + + # if go-md2man is available, pre-generate the man pages + ./docs/man/md2man-all.sh -q || true + # TODO decide if it's worth getting go-md2man in _each_ builder environment to avoid this + + # TODO add a configurable knob for _which_ rpms to build so we don't have to modify the file or build all of them every time we need to test + for dir in contrib/builder/rpm/*/; do + version="$(basename "$dir")" + suite="${version##*-}" + + image="dockercore/builder-rpm:$version" + if ! docker inspect "$image" &> /dev/null; then + ( set -x && docker build -t "$image" "$dir" ) + fi + + mkdir -p "$DEST/$version" + cat > "$DEST/$version/Dockerfile.build" <<-EOF + FROM $image + COPY . /usr/src/${rpmName} + RUN mkdir -p /root/rpmbuild/SOURCES + WORKDIR /root/rpmbuild + RUN ln -sfv /usr/src/${rpmName}/hack/make/.build-rpm SPECS + RUN tar -cz -C /usr/src -f /root/rpmbuild/SOURCES/${rpmName}.tar.gz ${rpmName} + WORKDIR /root/rpmbuild/SPECS + RUN { echo '* $rpmDate $rpmPackager $rpmVersion-$rpmRelease'; echo '* Version: $VERSION'; } >> ${rpmName}.spec && tail >&2 ${rpmName}.spec + RUN rpmbuild -ba --define '_release $rpmRelease' --define '_version $rpmVersion' --define '_origversion $VERSION' ${rpmName}.spec + EOF + tempImage="docker-temp/build-rpm:$version" + ( set -x && docker build -t "$tempImage" -f $DEST/$version/Dockerfile.build . ) + docker run --rm "$tempImage" bash -c 'cd /root/rpmbuild && tar -c *RPMS' | tar -xvC "$DEST/$version" + docker rmi "$tempImage" + done + + source "$(dirname "$BASH_SOURCE")/.integration-daemon-stop" +) 2>&1 | tee -a $DEST/test.log diff --git a/hack/make/dind-dynbinary b/hack/make/dind-dynbinary deleted file mode 100644 index a577ca0763cde..0000000000000 --- a/hack/make/dind-dynbinary +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -DEST=$1 -DOCKERBIN=$DEST/../binary/docker -BUILDDIR=$DEST/../dynbinary/ -RPM_PATH=$DEST/../rpm/ - -build_rpm() { - if [ ! -x $DOCKERBIN ]; then - echo "No docker binary was found to execute. This step requires 'binary' to be run first." - exit 1 - fi - - $DOCKERBIN -d 2>/dev/null & - $DOCKERBIN build -t centos-build -f Dockerfile.centos ./ - $DOCKERBIN run -it --rm --privileged -v /go:/go -v /usr/local:/usr/local -e "KEEPBUNDLE=true" --name centos-build-container centos-build hack/make.sh dynbinary - - # turn off the docker daemon - $DOCKERBIN rmi centos-build - cat /var/run/docker.pid | xargs kill -} - -build_rpm diff --git a/hack/make/rpm b/hack/make/rpm deleted file mode 100644 index 6340f79131f07..0000000000000 --- a/hack/make/rpm +++ /dev/null @@ -1,193 +0,0 @@ -#!/bin/bash - -DEST=$1 -PACKAGE_NAME=${PACKAGE_NAME:-docker-engine} - -# XXX - The package version in CentOS gets messed up and inserts a '~' -# (including the single quote) if we use the same package -# version scheme as the deb packages. This doesn't work with -# rpmbuild. -PKGVERSION="${VERSION}" -# if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better -if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then - GIT_UNIX="$(git log -1 --pretty='%at')" - GIT_DATE="$(date --date "@$GIT_UNIX" +'%Y%m%d.%H%M%S')" - GIT_COMMIT="$(git log -1 --pretty='%h')" - GIT_VERSION="git${GIT_DATE}.0.${GIT_COMMIT}" - # GIT_VERSION is now something like 'git20150128.112847.0.17e840a' - PKGVERSION="$PKGVERSION~$GIT_VERSION" -fi - -# $ dpkg --compare-versions 1.5.0 gt 1.5.0~rc1 && echo true || echo false -# true -# $ dpkg --compare-versions 1.5.0~rc1 gt 1.5.0~git20150128.112847.17e840a && echo true || echo false -# true -# $ dpkg --compare-versions 1.5.0~git20150128.112847.17e840a gt 1.5.0~dev~git20150128.112847.17e840a && echo true || echo false -# true - -# ie, 1.5.0 > 1.5.0~rc1 > 1.5.0~git20150128.112847.17e840a > 1.5.0~dev~git20150128.112847.17e840a - -PACKAGE_ARCHITECTURE=`uname -i` - -PACKAGE_URL="http://www.docker.com/" -PACKAGE_MAINTAINER="support@docker.com" -PACKAGE_DESCRIPTION="Linux container runtime -Docker complements LXC with a high-level API which operates at the process -level. It runs unix processes with strong guarantees of isolation and -repeatability across servers. -Docker is a great building block for automating distributed systems: -large-scale web deployments, database clusters, continuous deployment systems, -private PaaS, service-oriented architectures, etc." -PACKAGE_LICENSE="Apache-2.0" - -# bundle the RPM using FPM -- we may want to change this to rpmbuild at some point -bundle_rpm() { - DIR=$DEST/build - - # Include our udev rules - mkdir -p $DIR/etc/udev/rules.d - cp contrib/udev/80-docker.rules $DIR/etc/udev/rules.d/ - - mkdir -p $DIR/usr/lib/systemd/system - cp contrib/init/systemd/docker.{service,socket} $DIR/usr/lib/systemd/system - - cat > $DIR/usr/lib/systemd/system/docker.service <<'EOF' -[Unit] -Description=Docker Application Container Engine -Documentation=http://docs.docker.com -After=network.target docker.socket -Requires=docker.socket - -[Service] -Type=notify -EnvironmentFile=-/etc/sysconfig/docker -EnvironmentFile=-/etc/sysconfig/docker-storage -ExecStart=/usr/bin/docker -d $OPTIONS $DOCKER_STORAGE_OPTIONS -LimitNOFILE=1048576 -LimitNPROC=1048576 -MountFlags=private - -[Install] -WantedBy=multi-user.target -EOF - - mkdir -p $DIR/etc/sysconfig - cat > $DIR/etc/sysconfig/docker <<'EOF' -# /etc/sysconfig/docker - -# Modify these options if you want to change the way the docker daemon runs -OPTIONS=--selinux-enabled -H fd:// - -# Location used for temporary files, such as those created by -# docker load and build operations. Default is /var/lib/docker/tmp -# Can be overriden by setting the following environment variable. -# DOCKER_TMPDIR=/var/tmp -EOF - -cat > $DIR/etc/sysconfig/docker-storage <<'EOF' -# By default, Docker uses a loopback-mounted sparse file in -# /var/lib/docker. The loopback makes it slower, and there are some -# restrictive defaults, such as 100GB max storage. - -# If your installation did not set a custom storage for Docker, you -# may do it below. - -# Example: Use a custom pair of raw logical volumes (one for metadata, -# one for data). -# DOCKER_STORAGE_OPTIONS = --storage-opt dm.metadatadev=/dev/mylogvol/my-docker-metadata --storage-opt dm.datadev=/dev/mylogvol/my-docker-data - -DOCKER_STORAGE_OPTIONS= -EOF - - # Include contributed completions - mkdir -p $DIR/etc/bash_completion.d - cp contrib/completion/bash/docker $DIR/etc/bash_completion.d/ - mkdir -p $DIR/usr/share/zsh/vendor-completions - cp contrib/completion/zsh/_docker $DIR/usr/share/zsh/vendor-completions/ - mkdir -p $DIR/etc/fish/completions - cp contrib/completion/fish/docker.fish $DIR/etc/fish/completions/ - - # Include contributed man pages - docs/man/md2man-all.sh -q - manRoot="$DIR/usr/share/man" - mkdir -p "$manRoot" - for manDir in docs/man/man?; do - manBase="$(basename "$manDir")" # "man1" - for manFile in "$manDir"/*; do - manName="$(basename "$manFile")" # "docker-build.1" - mkdir -p "$manRoot/$manBase" - gzip -c "$manFile" > "$manRoot/$manBase/$manName.gz" - done - done - - # Copy the binary - # This will fail if the dynbinary bundle hasn't been built - mkdir -p $DIR/usr/bin - cp $DEST/../dynbinary/docker-$VERSION $DIR/usr/bin/docker - cp $DEST/../dynbinary/dockerinit-$VERSION $DIR/usr/bin/dockerinit - - # Generate postinst/prerm/postrm scripts - cat > $DEST/postinst <<'EOF' -EOF - - cat > $DEST/preinst <<'EOF' -if ! getent group docker > /dev/null; then - groupadd --system docker -fi -EOF - - cat > $DEST/prerm <<'EOF' -EOF - - cat > $DEST/postrm <<'EOF' -## In case this system is running systemd, we make systemd reload the unit files -## to pick up changes. -#if [ -d /run/systemd/system ] ; then -# systemctl --system daemon-reload > /dev/null || true -#fi -EOF - - chmod +x $DEST/postinst $DEST/prerm $DEST/postrm $DEST/preinst - - ( - # switch directories so we create *.deb in the right folder - cd $DEST - - # create PACKAGE_NAME-VERSION package - fpm -s dir -C $DIR \ - --name $PACKAGE_NAME-$VERSION --version "$PKGVERSION" \ - --epoch 7 \ - --before-install $DEST/preinst \ - --after-install $DEST/postinst \ - --before-remove $DEST/prerm \ - --after-remove $DEST/postrm \ - --architecture "$PACKAGE_ARCHITECTURE" \ - --prefix / \ - --depends iptables \ - --depends xz \ - --depends "systemd >= 208-20" \ - --depends "device-mapper-libs >= 7:1.02.90-1" \ - --depends "device-mapper-event-libs >= 7:1.02.90-1" \ - --depends libselinux \ - --depends libsepol \ - --depends sqlite \ - --description "$PACKAGE_DESCRIPTION" \ - --maintainer "$PACKAGE_MAINTAINER" \ - --conflicts docker \ - --conflicts docker-io \ - --conflicts lxc-docker-virtual-package \ - --conflicts lxc-docker \ - --url "$PACKAGE_URL" \ - --license "$PACKAGE_LICENSE" \ - --config-files etc/sysconfig \ - --config-files etc/udev/rules.d/80-docker.rules \ - --rpm-compression gzip \ - -t rpm . - ) - - # clean up after ourselves so we have a clean output directory - rm $DEST/postinst $DEST/prerm $DEST/postrm $DEST/preinst - rm -r $DIR -} - -bundle_rpm From 54662eae10923a0aca03ffddc1a30b7d25431c79 Mon Sep 17 00:00:00 2001 From: Doug Davis Date: Tue, 5 May 2015 13:00:20 -0700 Subject: [PATCH 103/321] Fix RUN's error msg when it fails When RUN returns with a non-zero return code it prints the command that was executed as a Go []string: ``` INFO[0000] The command &{[/bin/sh -c noop a1 a2]} returned a non-zero code: 127 ``` instead it should look like this: ``` INFO[0000] The command "/bin/sh -c noop a1 a2" returned a non-zero code: 127 ``` Signed-off-by: Doug Davis --- builder/internals.go | 2 +- integration-cli/docker_cli_build_test.go | 17 +++++++++++++++++ runconfig/config.go | 5 +++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/builder/internals.go b/builder/internals.go index 452180f9023cc..93c8ef802394a 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -619,7 +619,7 @@ func (b *Builder) run(c *daemon.Container) error { // Wait for it to finish if ret, _ := c.WaitStop(-1 * time.Second); ret != 0 { return &jsonmessage.JSONError{ - Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.Config.Cmd, ret), + Message: fmt.Sprintf("The command %q returned a non-zero code: %d", b.Config.Cmd.ToString(), ret), Code: ret, } } diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 5247a11ec5996..2656f52d83c6d 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5384,3 +5384,20 @@ func (s *DockerSuite) TestBuildBadCmdFlag(c *check.C) { c.Fatalf("Bad output\nGot:%s\n\nExpected to contain:%s\n", out, exp) } } + +func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) { + // Test to make sure the bad command is quoted with just "s and + // not as a Go []string + name := "testbuildbadrunerrmsg" + _, out, err := buildImageWithOut(name, ` + FROM busybox + RUN badEXE a1 a2`, false) + if err == nil { + c.Fatal("Should have failed to build") + } + + exp := `The command \"/bin/sh -c badEXE a1 a2\" returned a non-zero code: 127"` + if !strings.Contains(out, exp) { + c.Fatalf("RUN doesn't have the correct output:\nGot:%s\nExpected:%s", out, exp) + } +} diff --git a/runconfig/config.go b/runconfig/config.go index 844958be2c3ca..8778d26125eff 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -3,6 +3,7 @@ package runconfig import ( "encoding/json" "io" + "strings" "github.com/docker/docker/nat" ) @@ -59,6 +60,10 @@ type Command struct { parts []string } +func (e *Command) ToString() string { + return strings.Join(e.parts, " ") +} + func (e *Command) MarshalJSON() ([]byte, error) { if e == nil { return []byte{}, nil From d2c4ee37c6a4114b33a915b7dae6de70e27e7965 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 6 May 2015 10:18:01 -0400 Subject: [PATCH 104/321] Fix LXC stop signals `lxc-stop` does not support sending arbitrary signals. By default, `lxc-stop -n ` would send `SIGPWR`. The lxc driver was always sending `lxc-stop -n -k`, which always sends `SIGKILL`. In this case `lxc-start` returns an exit code of `0`, regardless of what the container actually exited with. Because of this we must send signals directly to the process when we can. Also need to set quiet mode on `lxc-start` otherwise it reports an error on `stderr` when the container exits cleanly (ie, we didn't SIGKILL it), this error is picked up in the container logs... and isn't really an error. Also cleaned up some potential races for waitblocked test. Signed-off-by: Brian Goff --- daemon/execdriver/lxc/driver.go | 15 +++++++++---- integration-cli/docker_cli_wait_test.go | 28 ++++++++++++++++++------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/daemon/execdriver/lxc/driver.go b/daemon/execdriver/lxc/driver.go index 32e5b43ebc961..49db60874328f 100644 --- a/daemon/execdriver/lxc/driver.go +++ b/daemon/execdriver/lxc/driver.go @@ -127,6 +127,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba "lxc-start", "-n", c.ID, "-f", configPath, + "-q", } // From lxc>=1.1 the default behavior is to daemonize containers after start @@ -278,19 +279,20 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba oomKillNotification, err := notifyOnOOM(cgroupPaths) <-waitLock + exitCode := getExitCode(c) if err == nil { _, oomKill = <-oomKillNotification - logrus.Debugf("oomKill error %s waitErr %s", oomKill, waitErr) + logrus.Debugf("oomKill error: %v, waitErr: %v", oomKill, waitErr) } else { logrus.Warnf("Your kernel does not support OOM notifications: %s", err) } // check oom error - exitCode := getExitCode(c) if oomKill { exitCode = 137 } + return execdriver.ExitStatus{ExitCode: exitCode, OOMKilled: oomKill}, waitErr } @@ -468,7 +470,11 @@ func getExitCode(c *execdriver.Command) int { } func (d *driver) Kill(c *execdriver.Command, sig int) error { - return KillLxc(c.ID, sig) + if sig == 9 || c.ProcessConfig.Process == nil { + return KillLxc(c.ID, sig) + } + + return c.ProcessConfig.Process.Signal(syscall.Signal(sig)) } func (d *driver) Pause(c *execdriver.Command) error { @@ -528,7 +534,8 @@ func KillLxc(id string, sig int) error { if err == nil { output, err = exec.Command("lxc-kill", "-n", id, strconv.Itoa(sig)).CombinedOutput() } else { - output, err = exec.Command("lxc-stop", "-k", "-n", id, strconv.Itoa(sig)).CombinedOutput() + // lxc-stop does not take arbitrary signals like lxc-kill does + output, err = exec.Command("lxc-stop", "-k", "-n", id).CombinedOutput() } if err != nil { return fmt.Errorf("Err: %s Output: %s", err, output) diff --git a/integration-cli/docker_cli_wait_test.go b/integration-cli/docker_cli_wait_test.go index 21f04faf0f8cd..b7fb3fe958da4 100644 --- a/integration-cli/docker_cli_wait_test.go +++ b/integration-cli/docker_cli_wait_test.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "os/exec" "strings" "time" @@ -44,7 +45,7 @@ func (s *DockerSuite) TestWaitNonBlockedExitZero(c *check.C) { // blocking wait with 0 exit code func (s *DockerSuite) TestWaitBlockedExitZero(c *check.C) { - out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' SIGTERM; while true; do sleep 0.01; done") + out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 0' TERM; while true; do sleep 0.01; done") containerID := strings.TrimSpace(out) if err := waitRun(containerID); err != nil { @@ -107,7 +108,7 @@ func (s *DockerSuite) TestWaitNonBlockedExitRandom(c *check.C) { // blocking wait with random exit code func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) { - out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "trap 'exit 99' SIGTERM; while true; do sleep 0.01; done") + out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "trap 'exit 99' TERM; while true; do sleep 0.01; done") containerID := strings.TrimSpace(out) if err := waitRun(containerID); err != nil { c.Fatal(err) @@ -116,21 +117,34 @@ func (s *DockerSuite) TestWaitBlockedExitRandom(c *check.C) { c.Fatal(err) } - chWait := make(chan string) + chWait := make(chan error) + waitCmd := exec.Command(dockerBinary, "wait", containerID) + waitCmdOut := bytes.NewBuffer(nil) + waitCmd.Stdout = waitCmdOut + if err := waitCmd.Start(); err != nil { + c.Fatal(err) + } + go func() { - out, _, _ := runCommandWithOutput(exec.Command(dockerBinary, "wait", containerID)) - chWait <- out + chWait <- waitCmd.Wait() }() - time.Sleep(100 * time.Millisecond) dockerCmd(c, "stop", containerID) select { - case status := <-chWait: + case err := <-chWait: + if err != nil { + c.Fatal(err) + } + status, err := waitCmdOut.ReadString('\n') + if err != nil { + c.Fatal(err) + } if strings.TrimSpace(status) != "99" { c.Fatalf("expected exit 99, got %s", status) } case <-time.After(2 * time.Second): + waitCmd.Process.Kill() c.Fatal("timeout waiting for `docker wait` to exit") } } From 76bc44fb912c66498b8e6e714ce4aabd9eaef8dc Mon Sep 17 00:00:00 2001 From: Matt Bentley Date: Wed, 6 May 2015 10:37:29 -0400 Subject: [PATCH 105/321] Added nanorc for Dockerfiles Signed-off-by: Matt Bentley --- contrib/syntax/nano/Dockerfile.nanorc | 26 ++++++++++++++++++++++ contrib/syntax/nano/README.md | 32 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 contrib/syntax/nano/Dockerfile.nanorc create mode 100644 contrib/syntax/nano/README.md diff --git a/contrib/syntax/nano/Dockerfile.nanorc b/contrib/syntax/nano/Dockerfile.nanorc new file mode 100644 index 0000000000000..80e56dfb36cab --- /dev/null +++ b/contrib/syntax/nano/Dockerfile.nanorc @@ -0,0 +1,26 @@ +## Syntax highlighting for Dockerfiles +syntax "Dockerfile" "Dockerfile[^/]*$" + +## Keywords +icolor red "^(FROM|MAINTAINER|RUN|CMD|LABEL|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ONBUILD)[[:space:]]" + +## Brackets & parenthesis +color brightgreen "(\(|\)|\[|\])" + +## Double ampersand +color brightmagenta "&&" + +## Comments +icolor cyan "^[[:space:]]*#.*$" + +## Blank space at EOL +color ,green "[[:space:]]+$" + +## Strings, single-quoted +color brightwhite "'([^']|(\\'))*'" "%[qw]\{[^}]*\}" "%[qw]\([^)]*\)" "%[qw]<[^>]*>" "%[qw]\[[^]]*\]" "%[qw]\$[^$]*\$" "%[qw]\^[^^]*\^" "%[qw]![^!]*!" + +## Strings, double-quoted +color brightwhite ""([^"]|(\\"))*"" "%[QW]?\{[^}]*\}" "%[QW]?\([^)]*\)" "%[QW]?<[^>]*>" "%[QW]?\[[^]]*\]" "%[QW]?\$[^$]*\$" "%[QW]?\^[^^]*\^" "%[QW]?![^!]*!" + +## Single and double quotes +color brightyellow "('|\")" diff --git a/contrib/syntax/nano/README.md b/contrib/syntax/nano/README.md new file mode 100644 index 0000000000000..5985208b0995d --- /dev/null +++ b/contrib/syntax/nano/README.md @@ -0,0 +1,32 @@ +Dockerfile.nanorc +================= + +Dockerfile syntax highlighting for nano + +Single User Installation +------------------------ +1. Create a nano syntax directory in your home directory: + * `mkdir -p ~/.nano/syntax` + +2. Copy `Dockerfile.nanorc` to` ~/.nano/syntax/` + * `cp Dockerfile.nanorc ~/.nano/syntax/` + +3. Add the following to your `~/.nanorc` to tell nano where to find the `Dockerfile.nanorc` file + ``` +## Dockerfile files +include "~/.nano/syntax/Dockerfile.nanorc" + ``` + +System Wide Installation +------------------------ +1. Create a nano syntax directory: + * `mkdir /usr/local/share/nano` + +2. Copy `Dockerfile.nanorc` to `/usr/local/share/nano` + * `cp Dockerfile.nanorc /usr/local/share/nano/` + +3. Add the following to your `/etc/nanorc`: + ``` +## Dockerfile files +include "/usr/local/share/nano/Dockerfile.nanorc" + ``` From 7c574b9e9d62f14c8d73ea358a557a771dcd8d4d Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 29 Apr 2015 13:48:30 -0400 Subject: [PATCH 106/321] Move ChunkedEncoding from integration Signed-off-by: Brian Goff --- integration-cli/docker_api_containers_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index f9da6fbcbd316..e4e3a4e9daf86 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "io" "net/http" + "net/http/httputil" "os" "os/exec" "strings" @@ -1150,3 +1151,54 @@ func (s *DockerSuite) TestContainerApiDeleteRemoveVolume(c *check.C) { c.Fatalf("expected to get ErrNotExist error, got %v", err) } } + +// Regression test for https://github.com/docker/docker/issues/6231 +func (s *DockerSuite) TestContainersApiChunkedEncoding(c *check.C) { + out, _ := dockerCmd(c, "create", "-v", "/foo", "busybox", "true") + id := strings.TrimSpace(out) + + conn, err := sockConn(time.Duration(10 * time.Second)) + if err != nil { + c.Fatal(err) + } + client := httputil.NewClientConn(conn, nil) + defer client.Close() + + bindCfg := strings.NewReader(`{"Binds": ["/tmp:/foo"]}`) + req, err := http.NewRequest("POST", "/containers/"+id+"/start", bindCfg) + if err != nil { + c.Fatal(err) + } + req.Header.Set("Content-Type", "application/json") + // This is a cheat to make the http request do chunked encoding + // Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite + // https://golang.org/src/pkg/net/http/request.go?s=11980:12172 + req.ContentLength = -1 + + resp, err := client.Do(req) + if err != nil { + c.Fatalf("error starting container with chunked encoding: %v", err) + } + resp.Body.Close() + if resp.StatusCode != 204 { + c.Fatalf("expected status code 204, got %d", resp.StatusCode) + } + + out, err = inspectFieldJSON(id, "HostConfig.Binds") + if err != nil { + c.Fatal(err) + } + + var binds []string + if err := json.NewDecoder(strings.NewReader(out)).Decode(&binds); err != nil { + c.Fatal(err) + } + if len(binds) != 1 { + c.Fatalf("got unexpected binds: %v", binds) + } + + expected := "/tmp:/foo" + if binds[0] != expected { + c.Fatalf("got incorrect bind spec, wanted %s, got: %s", expected, binds[0]) + } +} From ca8fa6e46d9eb93e652e4045e418e41d635c6b51 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Mon, 4 May 2015 13:58:21 -0600 Subject: [PATCH 107/321] Allow download-frozen-image.sh to work on user images too To account for "/" not working in filenames, we replace it with "_" for our temporary files (that exist only to emulate Bash 4's associative arrays in Bash 3). Signed-off-by: Andrew "Tianon" Page --- contrib/download-frozen-image.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/contrib/download-frozen-image.sh b/contrib/download-frozen-image.sh index 8a2bb501222b6..29d7ff59fd8f3 100755 --- a/contrib/download-frozen-image.sh +++ b/contrib/download-frozen-image.sh @@ -42,6 +42,8 @@ while [ $# -gt 0 ]; do [ "$tag" != "$imageTag" ] || tag='latest' tag="${tag%@*}" + imageFile="${image//\//_}" # "/" can't be in filenames :) + token="$(curl -sSL -o /dev/null -D- -H 'X-Docker-Token: true' "https://index.docker.io/v1/repositories/$image/images" | tr -d '\r' | awk -F ': *' '$1 == "X-Docker-Token" { print $2 }')" if [ -z "$imageId" ]; then @@ -60,12 +62,12 @@ while [ $# -gt 0 ]; do ancestry=( ${ancestryJson//[\[\] \"]/} ) unset IFS - if [ -s "$dir/tags-$image.tmp" ]; then - echo -n ', ' >> "$dir/tags-$image.tmp" + if [ -s "$dir/tags-$imageFile.tmp" ]; then + echo -n ', ' >> "$dir/tags-$imageFile.tmp" else images=( "${images[@]}" "$image" ) fi - echo -n '"'"$tag"'": "'"$imageId"'"' >> "$dir/tags-$image.tmp" + echo -n '"'"$tag"'": "'"$imageId"'"' >> "$dir/tags-$imageFile.tmp" echo "Downloading '$imageTag' (${#ancestry[@]} layers)..." for imageId in "${ancestry[@]}"; do @@ -90,10 +92,12 @@ done echo -n '{' > "$dir/repositories" firstImage=1 for image in "${images[@]}"; do + imageFile="${image//\//_}" # "/" can't be in filenames :) + [ "$firstImage" ] || echo -n ',' >> "$dir/repositories" firstImage= echo -n $'\n\t' >> "$dir/repositories" - echo -n '"'"$image"'": { '"$(cat "$dir/tags-$image.tmp")"' }' >> "$dir/repositories" + echo -n '"'"$image"'": { '"$(cat "$dir/tags-$imageFile.tmp")"' }' >> "$dir/repositories" done echo -n $'\n}\n' >> "$dir/repositories" From 72a0272a62f881fefb8dea5986b865fea36113ab Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 6 May 2015 20:56:12 +0200 Subject: [PATCH 108/321] Fix typo in the api remote reference for links Signed-off-by: Antonio Murdaca --- docs/sources/reference/api/docker_remote_api_v1.15.md | 4 ++-- docs/sources/reference/api/docker_remote_api_v1.16.md | 4 ++-- docs/sources/reference/api/docker_remote_api_v1.17.md | 4 ++-- docs/sources/reference/api/docker_remote_api_v1.18.md | 4 ++-- docs/sources/reference/api/docker_remote_api_v1.19.md | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/sources/reference/api/docker_remote_api_v1.15.md b/docs/sources/reference/api/docker_remote_api_v1.15.md index 8fcf8cb187ed2..e4fe5074d9a2c 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.15.md +++ b/docs/sources/reference/api/docker_remote_api_v1.15.md @@ -207,8 +207,8 @@ Json Parameters: volume for the container), `host_path:container_path` (to bind-mount a host path into the container), or `host_path:container_path:ro` (to make the bind-mount read-only inside the container). - - **Links** - A list of links for the container. Each link entry should be of - of the form "container_name:alias". + - **Links** - A list of links for the container. Each link entry should be + in the form of "container_name:alias". - **LxcConf** - LXC specific configurations. These configurations will only work when using the `lxc` execution driver. - **PortBindings** - A map of exposed container ports and the host port they diff --git a/docs/sources/reference/api/docker_remote_api_v1.16.md b/docs/sources/reference/api/docker_remote_api_v1.16.md index 9c6159b9d5023..df8e5be13eb73 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.16.md +++ b/docs/sources/reference/api/docker_remote_api_v1.16.md @@ -207,8 +207,8 @@ Json Parameters: volume for the container), `host_path:container_path` (to bind-mount a host path into the container), or `host_path:container_path:ro` (to make the bind-mount read-only inside the container). - - **Links** - A list of links for the container. Each link entry should be of - of the form "container_name:alias". + - **Links** - A list of links for the container. Each link entry should be + in the form of "container_name:alias". - **LxcConf** - LXC specific configurations. These configurations will only work when using the `lxc` execution driver. - **PortBindings** - A map of exposed container ports and the host port they diff --git a/docs/sources/reference/api/docker_remote_api_v1.17.md b/docs/sources/reference/api/docker_remote_api_v1.17.md index 80f4fccf00301..d8ef81c0fbbd6 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.17.md +++ b/docs/sources/reference/api/docker_remote_api_v1.17.md @@ -207,8 +207,8 @@ Json Parameters: volume for the container), `host_path:container_path` (to bind-mount a host path into the container), or `host_path:container_path:ro` (to make the bind-mount read-only inside the container). - - **Links** - A list of links for the container. Each link entry should be of - of the form "container_name:alias". + - **Links** - A list of links for the container. Each link entry should be + in the form of "container_name:alias". - **LxcConf** - LXC specific configurations. These configurations will only work when using the `lxc` execution driver. - **PortBindings** - A map of exposed container ports and the host port they diff --git a/docs/sources/reference/api/docker_remote_api_v1.18.md b/docs/sources/reference/api/docker_remote_api_v1.18.md index a91ca8417a121..71ab4176979ee 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.18.md +++ b/docs/sources/reference/api/docker_remote_api_v1.18.md @@ -218,8 +218,8 @@ Json Parameters: volume for the container), `host_path:container_path` (to bind-mount a host path into the container), or `host_path:container_path:ro` (to make the bind-mount read-only inside the container). - - **Links** - A list of links for the container. Each link entry should be of - of the form `container_name:alias`. + - **Links** - A list of links for the container. Each link entry should be + in the form of `container_name:alias`. - **LxcConf** - LXC specific configurations. These configurations will only work when using the `lxc` execution driver. - **PortBindings** - A map of exposed container ports and the host port they diff --git a/docs/sources/reference/api/docker_remote_api_v1.19.md b/docs/sources/reference/api/docker_remote_api_v1.19.md index 1a321fe736317..f2f71245eb680 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.19.md +++ b/docs/sources/reference/api/docker_remote_api_v1.19.md @@ -222,8 +222,8 @@ Json Parameters: volume for the container), `host_path:container_path` (to bind-mount a host path into the container), or `host_path:container_path:ro` (to make the bind-mount read-only inside the container). - - **Links** - A list of links for the container. Each link entry should be of - of the form `container_name:alias`. + - **Links** - A list of links for the container. Each link entry should be + in the form of `container_name:alias`. - **LxcConf** - LXC specific configurations. These configurations will only work when using the `lxc` execution driver. - **PortBindings** - A map of exposed container ports and the host port they From 589de35651ce8c91a2f01be2a5c99274d548d9ae Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Fri, 24 Apr 2015 00:08:41 +0200 Subject: [PATCH 109/321] Logs with follow=1 immediately send HTTP response Signed-off-by: Anthony Baire Signed-off-by: Arnaud Porterie --- daemon/logs.go | 5 +++++ integration-cli/docker_api_logs_test.go | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/daemon/logs.go b/daemon/logs.go index 79d4044bbe20b..ae3e997311741 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -129,6 +129,11 @@ func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) er errors := make(chan error, 2) wg := sync.WaitGroup{} + // write an empty chunk of data (this is to ensure that the + // HTTP Response is sent immediatly, even if the container has + // not yet produced any data) + outStream.Write(nil) + if config.UseStdout { wg.Add(1) stdoutPipe := container.StdoutLogPipe() diff --git a/integration-cli/docker_api_logs_test.go b/integration-cli/docker_api_logs_test.go index f9284494d2e88..d77ddef30a427 100644 --- a/integration-cli/docker_api_logs_test.go +++ b/integration-cli/docker_api_logs_test.go @@ -60,3 +60,25 @@ func (s *DockerSuite) TestLogsApiNoStdoutNorStderr(c *check.C) { c.Fatalf("Expected %s, got %s", expected, string(body[:])) } } + +// Regression test for #12704 +func (s *DockerSuite) TestLogsApiFollowEmptyOutput(c *check.C) { + defer deleteAllContainers() + name := "logs_test" + t0 := time.Now() + runCmd := exec.Command(dockerBinary, "run", "-d", "-t", "--name", name, "busybox", "sleep", "10") + if out, _, err := runCommandWithOutput(runCmd); err != nil { + c.Fatal(out, err) + } + + _, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1&stderr=1&tail=all", name), bytes.NewBuffer(nil), "") + t1 := time.Now() + body.Close() + if err != nil { + c.Fatal(err) + } + elapsed := t1.Sub(t0).Seconds() + if elapsed > 5.0 { + c.Fatalf("HTTP response was not immediate (elapsed %.1fs)", elapsed) + } +} From e35b025aa61e7d8db04a9973967b7109f742593a Mon Sep 17 00:00:00 2001 From: jhowardmsft Date: Fri, 24 Apr 2015 11:12:56 -0700 Subject: [PATCH 110/321] Windows: Split ContainerExecCreate Signed-off-by: John Howard --- daemon/exec.go | 6 +++--- daemon/exec_linux.go | 18 ++++++++++++++++++ daemon/exec_windows.go | 9 +++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 daemon/exec_linux.go create mode 100644 daemon/exec_windows.go diff --git a/daemon/exec.go b/daemon/exec.go index 5febf083a6f70..fc4e8eab4e5eb 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -9,7 +9,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/execdriver" - "github.com/docker/docker/daemon/execdriver/lxc" "github.com/docker/docker/pkg/broadcastwriter" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/stringid" @@ -111,8 +110,9 @@ func (d *Daemon) getActiveContainer(name string) (*Container, error) { func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, error) { - if strings.HasPrefix(d.execDriver.Name(), lxc.DriverName) { - return "", lxc.ErrExec + // Not all drivers support Exec (LXC for example) + if err := checkExecSupport(d.execDriver.Name()); err != nil { + return "", err } container, err := d.getActiveContainer(config.Container) diff --git a/daemon/exec_linux.go b/daemon/exec_linux.go new file mode 100644 index 0000000000000..a360326327181 --- /dev/null +++ b/daemon/exec_linux.go @@ -0,0 +1,18 @@ +// +build linux + +package daemon + +import ( + "strings" + + "github.com/docker/docker/daemon/execdriver/lxc" +) + +// checkExecSupport returns an error if the exec driver does not support exec, +// or nil if it is supported. +func checkExecSupport(drivername string) error { + if strings.HasPrefix(drivername, lxc.DriverName) { + return lxc.ErrExec + } + return nil +} diff --git a/daemon/exec_windows.go b/daemon/exec_windows.go new file mode 100644 index 0000000000000..d6f244e6d6499 --- /dev/null +++ b/daemon/exec_windows.go @@ -0,0 +1,9 @@ +// +build windows + +package daemon + +// checkExecSupport returns an error if the exec driver does not support exec, +// or nil if it is supported. +func checkExecSupport(DriverName string) error { + return nil +} From 596e91638cbe8b583dff4fdfc5a52e9d3840b20f Mon Sep 17 00:00:00 2001 From: Arnaud Porterie Date: Wed, 6 May 2015 14:35:10 -0700 Subject: [PATCH 111/321] Remove unused Dockerfile instruction INSERT Signed-off-by: Arnaud Porterie --- builder/command/command.go | 2 -- builder/dispatchers.go | 5 ----- builder/evaluator.go | 1 - builder/parser/parser.go | 1 - 4 files changed, 9 deletions(-) diff --git a/builder/command/command.go b/builder/command/command.go index 16544f0267c07..8e5d980321692 100644 --- a/builder/command/command.go +++ b/builder/command/command.go @@ -16,7 +16,6 @@ const ( Expose = "expose" Volume = "volume" User = "user" - Insert = "insert" ) // Commands is list of all Dockerfile commands @@ -35,5 +34,4 @@ var Commands = map[string]struct{}{ Expose: {}, Volume: {}, User: {}, - Insert: {}, } diff --git a/builder/dispatchers.go b/builder/dispatchers.go index 7d1dab640742a..4d4c12396386d 100644 --- a/builder/dispatchers.go +++ b/builder/dispatchers.go @@ -512,8 +512,3 @@ func volume(b *Builder, args []string, attributes map[string]bool, original stri } return nil } - -// INSERT is no longer accepted, but we still parse it. -func insert(b *Builder, args []string, attributes map[string]bool, original string) error { - return fmt.Errorf("INSERT has been deprecated. Please use ADD instead") -} diff --git a/builder/evaluator.go b/builder/evaluator.go index bdcc6b29a853f..49e4a764816dc 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -71,7 +71,6 @@ func init() { command.Expose: expose, command.Volume: volume, command.User: user, - command.Insert: insert, } } diff --git a/builder/parser/parser.go b/builder/parser/parser.go index f68c710c06e39..2260cd5270e5a 100644 --- a/builder/parser/parser.go +++ b/builder/parser/parser.go @@ -61,7 +61,6 @@ func init() { command.Entrypoint: parseMaybeJSON, command.Expose: parseStringsWhitespaceDelimited, command.Volume: parseMaybeJSONToList, - command.Insert: parseIgnore, } } From 74121a42118750e560e772a3fee33e9d7bd903d0 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 7 May 2015 01:49:16 +0200 Subject: [PATCH 112/321] Do not check and return strconv.Atoi error in api container restart, regression Signed-off-by: Antonio Murdaca --- api/server/server.go | 5 +---- integration-cli/docker_api_containers_test.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/api/server/server.go b/api/server/server.go index ab236f876f153..e1fcc74e8e802 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -917,10 +917,7 @@ func (s *Server) postContainersRestart(version version.Version, w http.ResponseW return fmt.Errorf("Missing parameter") } - timeout, err := strconv.Atoi(r.Form.Get("t")) - if err != nil { - return err - } + timeout, _ := strconv.Atoi(r.Form.Get("t")) if err := s.daemon.ContainerRestart(vars["name"], timeout); err != nil { return err diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index e4e3a4e9daf86..11956108adaf8 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -899,6 +899,25 @@ func (s *DockerSuite) TestContainerApiRestart(c *check.C) { } } +func (s *DockerSuite) TestContainerApiRestartNotimeoutParam(c *check.C) { + name := "test-api-restart-no-timeout-param" + runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + if err != nil { + c.Fatalf("Error on container creation: %v, output: %q", err, out) + } + id := strings.TrimSpace(out) + c.Assert(waitRun(id), check.IsNil) + + status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil) + c.Assert(status, check.Equals, http.StatusNoContent) + c.Assert(err, check.IsNil) + + if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil { + c.Fatal(err) + } +} + func (s *DockerSuite) TestContainerApiStart(c *check.C) { name := "testing-start" config := map[string]interface{}{ From 867eed8f3586c81b32dc9f85208692e9e1c9909a Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Wed, 6 May 2015 17:39:10 -0600 Subject: [PATCH 113/321] Fix build-deb This fixes the part of #12996 that I forgot. :angel: This also fixes a minor path issue (there's no `libexec` in Debian), and fixes a minor bug with the `debVersion` parsing. Signed-off-by: Andrew "Tianon" Page --- ...ker-core.bash-completion => docker-engine.bash-completion} | 0 ...ocker-core.docker.default => docker-engine.docker.default} | 0 .../{docker-core.docker.init => docker-engine.docker.init} | 0 ...ocker-core.docker.upstart => docker-engine.docker.upstart} | 0 .../.build-deb/{docker-core.install => docker-engine.install} | 0 .../{docker-core.manpages => docker-engine.manpages} | 0 .../{docker-core.postinst => docker-engine.postinst} | 0 hack/make/.build-deb/{docker-core.udev => docker-engine.udev} | 0 hack/make/.build-deb/rules | 4 ++-- hack/make/build-deb | 2 +- 10 files changed, 3 insertions(+), 3 deletions(-) rename hack/make/.build-deb/{docker-core.bash-completion => docker-engine.bash-completion} (100%) rename hack/make/.build-deb/{docker-core.docker.default => docker-engine.docker.default} (100%) rename hack/make/.build-deb/{docker-core.docker.init => docker-engine.docker.init} (100%) rename hack/make/.build-deb/{docker-core.docker.upstart => docker-engine.docker.upstart} (100%) rename hack/make/.build-deb/{docker-core.install => docker-engine.install} (100%) rename hack/make/.build-deb/{docker-core.manpages => docker-engine.manpages} (100%) rename hack/make/.build-deb/{docker-core.postinst => docker-engine.postinst} (100%) rename hack/make/.build-deb/{docker-core.udev => docker-engine.udev} (100%) diff --git a/hack/make/.build-deb/docker-core.bash-completion b/hack/make/.build-deb/docker-engine.bash-completion similarity index 100% rename from hack/make/.build-deb/docker-core.bash-completion rename to hack/make/.build-deb/docker-engine.bash-completion diff --git a/hack/make/.build-deb/docker-core.docker.default b/hack/make/.build-deb/docker-engine.docker.default similarity index 100% rename from hack/make/.build-deb/docker-core.docker.default rename to hack/make/.build-deb/docker-engine.docker.default diff --git a/hack/make/.build-deb/docker-core.docker.init b/hack/make/.build-deb/docker-engine.docker.init similarity index 100% rename from hack/make/.build-deb/docker-core.docker.init rename to hack/make/.build-deb/docker-engine.docker.init diff --git a/hack/make/.build-deb/docker-core.docker.upstart b/hack/make/.build-deb/docker-engine.docker.upstart similarity index 100% rename from hack/make/.build-deb/docker-core.docker.upstart rename to hack/make/.build-deb/docker-engine.docker.upstart diff --git a/hack/make/.build-deb/docker-core.install b/hack/make/.build-deb/docker-engine.install similarity index 100% rename from hack/make/.build-deb/docker-core.install rename to hack/make/.build-deb/docker-engine.install diff --git a/hack/make/.build-deb/docker-core.manpages b/hack/make/.build-deb/docker-engine.manpages similarity index 100% rename from hack/make/.build-deb/docker-core.manpages rename to hack/make/.build-deb/docker-engine.manpages diff --git a/hack/make/.build-deb/docker-core.postinst b/hack/make/.build-deb/docker-engine.postinst similarity index 100% rename from hack/make/.build-deb/docker-core.postinst rename to hack/make/.build-deb/docker-engine.postinst diff --git a/hack/make/.build-deb/docker-core.udev b/hack/make/.build-deb/docker-engine.udev similarity index 100% rename from hack/make/.build-deb/docker-core.udev rename to hack/make/.build-deb/docker-engine.udev diff --git a/hack/make/.build-deb/rules b/hack/make/.build-deb/rules index 22dab31d12abc..fe19b729eac87 100755 --- a/hack/make/.build-deb/rules +++ b/hack/make/.build-deb/rules @@ -21,8 +21,8 @@ override_dh_strip: override_dh_auto_install: mkdir -p debian/docker-engine/usr/bin cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/docker)" debian/docker-engine/usr/bin/docker - mkdir -p debian/docker-engine/usr/libexec/docker - cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/dockerinit)" debian/docker-engine/usr/libexec/docker/dockerinit + mkdir -p debian/docker-engine/usr/lib/docker + cp -aT "$$(readlink -f bundles/$(VERSION)/dynbinary/dockerinit)" debian/docker-engine/usr/lib/docker/dockerinit override_dh_installinit: # use "docker" as our service name, not "docker-engine" diff --git a/hack/make/build-deb b/hack/make/build-deb index 90a2b98b85267..a347f0e851ee5 100644 --- a/hack/make/build-deb +++ b/hack/make/build-deb @@ -9,7 +9,7 @@ DEST=$1 # TODO consider using frozen images for the dockercore/builder-deb tags - debVersion="${VERSION//-/'~'}" + debVersion="${VERSION//-/~}" # if we have a "-dev" suffix or have change in Git, let's make this package version more complex so it works better if [[ "$VERSION" == *-dev ]] || [ -n "$(git status --porcelain)" ]; then gitUnix="$(git log -1 --pretty='%at')" From 9da3a848abea56dff960baa8428dc073b70ec1de Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Mon, 4 May 2015 05:25:47 +0000 Subject: [PATCH 114/321] Add a userguide to cover the uses of Hub before creating new repositories Signed-off-by: Sven Dowideit --- docs/mkdocs.yml | 3 +- .../docker-hub/hub-images/dashboard.png | Bin 0 -> 140981 bytes docs/sources/docker-hub/hub-images/groups.png | Bin 61631 -> 70648 bytes docs/sources/docker-hub/hub-images/hub.png | Bin 27837 -> 68579 bytes docs/sources/docker-hub/hub-images/invite.png | Bin 41551 -> 40459 bytes docs/sources/docker-hub/hub-images/orgs.png | Bin 36206 -> 45997 bytes docs/sources/docker-hub/hub-images/repos.png | Bin 33179 -> 63242 bytes docs/sources/docker-hub/index.md | 21 +++-- docs/sources/docker-hub/repos.md | 86 ++++++++---------- docs/sources/docker-hub/userguide.md | 57 ++++++++++++ 10 files changed, 112 insertions(+), 55 deletions(-) create mode 100644 docs/sources/docker-hub/hub-images/dashboard.png create mode 100644 docs/sources/docker-hub/userguide.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7438af582b5b6..1fc19dc963a57 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -75,7 +75,8 @@ pages: # Docker Hub docs: - ['docker-hub/index.md', 'Docker Hub', 'Docker Hub' ] - ['docker-hub/accounts.md', 'Docker Hub', 'Accounts'] -- ['docker-hub/repos.md', 'Docker Hub', 'Repositories'] +- ['docker-hub/userguide.md', 'Docker Hub', 'User Guide'] +- ['docker-hub/repos.md', 'Docker Hub', 'Your Repositories'] - ['docker-hub/builds.md', 'Docker Hub', 'Automated Builds'] - ['docker-hub/official_repos.md', 'Docker Hub', 'Official Repositories'] diff --git a/docs/sources/docker-hub/hub-images/dashboard.png b/docs/sources/docker-hub/hub-images/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..594c5d14571d4a61a7380f27a8227ce00b5b12ca GIT binary patch literal 140981 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
u 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 diff --git a/docs/sources/docker-hub/index.md b/docs/sources/docker-hub/index.md index 3651497e2c212..db6694d3d515e 100644 --- a/docs/sources/docker-hub/index.md +++ b/docs/sources/docker-hub/index.md @@ -4,20 +4,29 @@ page_keywords: Docker, docker, registry, accounts, plans, Dockerfile, Docker Hub # Docker Hub +The [Docker Hub](https://hub.docker.com) provides a cloud-based platform service +for distributed applications, including container image distribution and change +management, user and team collaboration, and lifecycle workflow automation. + ![DockerHub](/docker-hub/hub-images/hub.png) -## [Accounts](accounts/) +## [Finding and pulling images](./userguide.md) + +Find out how to [use the Docker Hub](./userguide.md) to find and pull Docker +images to run or build upon. + +## [Accounts](./accounts.md) -[Learn how to create](accounts/) a [Docker Hub](https://hub.docker.com) +[Learn how to create](./accounts.md) a Docker Hub account and manage your organizations and groups. -## [Repositories](repos/) +## [Your Repositories](./repos.md) Find out how to share your Docker images in [Docker Hub -repositories](repos/) and how to store and manage private images. +repositories](./repos.md) and how to store and manage private images. -## [Automated builds](builds/) +## [Automated builds](./builds.md) Learn how to automate your build and deploy pipeline with [Automated -Builds](builds/) +Builds](./builds.md) diff --git a/docs/sources/docker-hub/repos.md b/docs/sources/docker-hub/repos.md index a48040fb55806..67cf0431ecb5f 100644 --- a/docs/sources/docker-hub/repos.md +++ b/docs/sources/docker-hub/repos.md @@ -1,42 +1,37 @@ -page_title: Repositories and images on Docker Hub -page_description: Repositories and images on Docker Hub +page_title: Your Repositories on Docker Hub +page_description: Your Repositories on Docker Hub page_keywords: Docker, docker, registry, accounts, plans, Dockerfile, Docker Hub, webhooks, docs, documentation -# Repositories and images on Docker Hub +# Your Hub repositories -![repositories](/docker-hub/hub-images/repos.png) - -## Searching for repositories and images - -You can `search` for all the publicly available repositories and images using -Docker. - - $ docker search ubuntu +Docker Hub repositories make it possible for you to share images with co-workers, +customers or the Docker community at large. If you're building your images internally, +either on your own Docker daemon, or using your own Continuous integration services, +you can push them to a Docker Hub repository that you add to your Docker Hub user or +organization account. -This will show you a list of the currently available repositories on the -Docker Hub which match the provided keyword. +Alternativly, if the source code for your Docker image is on GitHub or Bitbucket, +you can use an "Automated build" repository, which is built by the Docker Hub +services. See the [automated builds documentation](./builds.md) to read about +the extra functionality provided by those services. -If a repository is private it won't be listed on the repository search -results. To see repository statuses, you can look at your [profile -page](https://hub.docker.com) on [Docker Hub](https://hub.docker.com). - -## Repositories +![repositories](/docker-hub/hub-images/repos.png) Your Docker Hub repositories have a number of useful features. -### Stars +## Stars Your repositories can be starred and you can star repositories in return. Stars are a way to show that you like a repository. They are also an easy way of bookmarking your favorites. -### Comments +## Comments You can interact with other members of the Docker community and maintainers by leaving comments on repositories. If you find any comments that are not appropriate, you can flag them for review. -### Collaborators and their role +## Collaborators and their role A collaborator is someone you want to give access to a private repository. Once designated, they can `push` and `pull` to your @@ -48,24 +43,9 @@ private to public. > A collaborator cannot add other collaborators. Only the owner of > the repository has administrative access. -You can also collaborate on Docker Hub with organizations and groups. -You can read more about that [here](accounts/). - -## Official Repositories - -The Docker Hub contains a number of [Official -Repositories](http://registry.hub.docker.com/official). These are -certified repositories from vendors and contributors to Docker. They -contain Docker images from vendors like Canonical, Oracle, and Red Hat -that you can use to build applications and services. - -If you use Official Repositories you know you're using a supported, -optimized and up-to-date image to power your applications. - -> **Note:** -> If you would like to contribute an Official Repository for your -> organization, see [Official Repositories on Docker -> Hub](/docker-hub/official_repos) for more information. +You can also assign more granular collaborator rights ("Read", "Write", or "Admin") +on Docker Hub by using organizations and groups. For more information +see the [accounts documentation](accounts/). ## Private repositories @@ -100,8 +80,15 @@ Hub](https://registry.hub.docker.com/plans/) plan. ## Webhooks -You can configure webhooks for your repositories on the Repository -Settings page. A webhook is called only after a successful `push` is +A webhook is an HTTP call-back triggered by a specific event. +You can use a Hub repository webhook to notify people, services, and other +applications after a new image is pushed to your repository (this also happens +for Automated builds). For example, you can trigger an automated test or +deployment to happen as soon as the image is available. + +To get started adding webhooks, go to the desired repository in the Hub, +and click "Webhooks" under the "Settings" box. +A webhook is called only after a successful `push` is made. The webhook calls are HTTP POST requests with a JSON payload similar to the example shown below. @@ -137,13 +124,9 @@ similar to the example shown below. } ``` -Webhooks allow you to notify people, services and other applications of -new updates to your images and repositories. To get started adding webhooks, -go to the desired repository in the Hub, and click "Webhooks" under the "Settings" -box. + -> **Note:** For testing, you can try an HTTP request tool like -> [requestb.in](http://requestb.in/). +For testing, you can try an HTTP request tool like [requestb.in](http://requestb.in/). > **Note**: The Docker Hub servers are currently in the IP range > `162.242.195.64 - 162.242.195.127`, so you can restrict your webhooks to @@ -161,7 +144,7 @@ in your chain. The first webhook in a chain will be called after a successful push. Subsequent URLs will be contacted after the callback has been validated. -#### Validating a callback +### Validating a callback In order to validate a callback in a webhook chain, you need to @@ -195,3 +178,10 @@ The following parameters are recognized in callback data: "context": "Continuous integration by Acme CI", "target_url": "http://ci.acme.com/results/afd339c1c3d27" } + +## Mark as unlisted + +By marking a repository as unlisted, you can create a publically pullable repository +which will not be in the Hub or commandline search. This allows you to have a limited +release, but does not restrict access to anyone that is told, or guesses the repository +name. diff --git a/docs/sources/docker-hub/userguide.md b/docs/sources/docker-hub/userguide.md new file mode 100644 index 0000000000000..7ace5f358b14f --- /dev/null +++ b/docs/sources/docker-hub/userguide.md @@ -0,0 +1,57 @@ +page_title: Docker Hub user guide +page_description: Docker Hub user guide +page_keywords: Docker, docker, registry, Docker Hub, docs, documentation + +# Using the Docker Hub + +Docker Hub is used to find and pull Docker images to run or build upon, and to +distribute and build images for other users to use. + +![your profile](/docker-hub/hub-images/dashboard.png) + +## Finding repositories and images + +There are two ways you can search for public repositories and images available +on the Docker Hub. You can use the "Search" tool on the Docker Hub website, or +you can `search` for all the repositories and images using the Docker commandline +tool: + + $ docker search ubuntu + +Both will show you a list of the currently available public repositories on the +Docker Hub which match the provided keyword. + +If a repository is private or marked as unlisted, it won't be in the repository +search results. To see all the repositories you have access to and their statuses, +you can look at your profile page on [Docker Hub](https://hub.docker.com). + +## Pulling, running and building images + +You can find more information on [working with Docker images](../userguide/dockerimages.md). + +## Official Repositories + +The Docker Hub contains a number of [Official +Repositories](http://registry.hub.docker.com/official). These are +certified repositories from vendors and contributors to Docker. They +contain Docker images from vendors like Canonical, Oracle, and Red Hat +that you can use to build applications and services. + +If you use Official Repositories you know you're using an optimized and +up-to-date image to power your applications. + +> **Note:** +> If you would like to contribute an Official Repository for your +> organization, see [Official Repositories on Docker +> Hub](/docker-hub/official_repos) for more information. + +## Building and shipping your own repositories and images + +The Docker Hub provides you and your team with a place to build and ship Docker images. + +Collections of Docker images are managed using repositories - + +You can configure two types of repositories to manage on the Docker Hub: +[Repositories](./repos.md), which allow you to push images to the Hub from your local Docker daemon, +and [Automated Builds](./builds.md), which allow you to configure GitHub or Bitbucket to +trigger the Hub to rebuild repositories when changes are made to the repository. From ceae5f54b3abe805b3323476dafb00595b064ed2 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Thu, 7 May 2015 10:11:02 +0800 Subject: [PATCH 115/321] update recommended kernel in checkKernel We already changed docs: https://github.com/docker/docker/pull/10652 Should change code as well. Signed-off-by: Qiang Huang --- daemon/daemon.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index 130fcc46f245c..0f692c7c685af 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1139,14 +1139,14 @@ func checkKernel() error { // test for specific functionalities. // Unfortunately we can't test for the feature "does not cause a kernel panic" // without actually causing a kernel panic, so we need this workaround until - // the circumstances of pre-3.8 crashes are clearer. + // the circumstances of pre-3.10 crashes are clearer. // For details see https://github.com/docker/docker/issues/407 if k, err := kernel.GetKernelVersion(); err != nil { logrus.Warnf("%s", err) } else { - if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { + if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 10, Minor: 0}) < 0 { if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { - logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String()) + logrus.Warnf("You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.10.0.", k.String()) } } } From 44cd599e29451647492b3a5341ba23252a69ca27 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Mon, 4 May 2015 13:49:28 -0400 Subject: [PATCH 116/321] Cleanup container reg for lxc special case The lxc code here is doing the exact same thing on calling execdriver.Terminate, so let's just use that. Also removes some dead comments originally introduced 50144aeb42283848db730b936d6b5b6332ec6565 but no longer relevant since we have restart policies. Signed-off-by: Brian Goff --- daemon/daemon.go | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index 130fcc46f245c..26dbd50e34f60 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -22,7 +22,6 @@ import ( "github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/daemon/execdriver/execdrivers" - "github.com/docker/docker/daemon/execdriver/lxc" "github.com/docker/docker/daemon/graphdriver" _ "github.com/docker/docker/daemon/graphdriver/vfs" "github.com/docker/docker/daemon/network" @@ -208,25 +207,16 @@ func (daemon *Daemon) register(container *Container, updateSuffixarray bool) err container.registerVolumes() - // FIXME: if the container is supposed to be running but is not, auto restart it? - // if so, then we need to restart monitor and init a new lock - // If the container is supposed to be running, make sure of it if container.IsRunning() { logrus.Debugf("killing old running container %s", container.ID) container.SetStopped(&execdriver.ExitStatus{ExitCode: 0}) - // We only have to handle this for lxc because the other drivers will ensure that - // no processes are left when docker dies - if container.ExecDriver == "" || strings.Contains(container.ExecDriver, "lxc") { - lxc.KillLxc(container.ID, 9) - } else { - // use the current driver and ensure that the container is dead x.x - cmd := &execdriver.Command{ - ID: container.ID, - } - daemon.execDriver.Terminate(cmd) + // use the current driver and ensure that the container is dead x.x + cmd := &execdriver.Command{ + ID: container.ID, } + daemon.execDriver.Terminate(cmd) if err := container.Unmount(); err != nil { logrus.Debugf("unmount error %s", err) From f133f11a7d25e6262558dd733afaa95ddd1c7aee Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Thu, 7 May 2015 11:55:58 +0800 Subject: [PATCH 117/321] add blkio.weight support We can use this to control block IO weight of a container. Signed-off-by: Qiang Huang --- daemon/container.go | 1 + daemon/daemon.go | 3 +++ daemon/execdriver/driver.go | 1 + daemon/execdriver/driver_linux.go | 1 + daemon/execdriver/lxc/lxc_template.go | 3 +++ docs/man/docker-create.1.md | 4 +++ docs/man/docker-run.1.md | 4 +++ .../reference/api/docker_remote_api_v1.19.md | 3 +++ docs/sources/reference/commandline/cli.md | 2 ++ docs/sources/reference/run.md | 25 +++++++++++++++++++ integration-cli/docker_cli_run_test.go | 14 +++++++++++ runconfig/hostconfig.go | 3 ++- runconfig/parse.go | 2 ++ 13 files changed, 65 insertions(+), 1 deletion(-) diff --git a/daemon/container.go b/daemon/container.go index 5c7d3a4e52952..b1e306086891a 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -383,6 +383,7 @@ func populateCommand(c *Container, env []string) error { CpusetCpus: c.hostConfig.CpusetCpus, CpusetMems: c.hostConfig.CpusetMems, CpuQuota: c.hostConfig.CpuQuota, + BlkioWeight: c.hostConfig.BlkioWeight, Rlimits: rlimits, OomKillDisable: c.hostConfig.OomKillDisable, } diff --git a/daemon/daemon.go b/daemon/daemon.go index 130fcc46f245c..03d6c59608190 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1184,6 +1184,9 @@ func (daemon *Daemon) verifyHostConfig(hostConfig *runconfig.HostConfig) ([]stri warnings = append(warnings, "Your kernel does not support CPU cfs quota. Quota discarded.") hostConfig.CpuQuota = 0 } + if hostConfig.BlkioWeight > 0 && (hostConfig.BlkioWeight < 10 || hostConfig.BlkioWeight > 1000) { + return warnings, fmt.Errorf("Range of blkio weight is from 10 to 1000.") + } return warnings, nil } diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 7827baa2668d0..8a00350368e33 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -106,6 +106,7 @@ type Resources struct { CpusetCpus string `json:"cpuset_cpus"` CpusetMems string `json:"cpuset_mems"` CpuQuota int64 `json:"cpu_quota"` + BlkioWeight int64 `json:"blkio_weight"` Rlimits []*ulimit.Rlimit `json:"rlimits"` OomKillDisable bool `json:"oom_kill_disable"` } diff --git a/daemon/execdriver/driver_linux.go b/daemon/execdriver/driver_linux.go index cdaa93af3443b..f3bbca3e5ca02 100644 --- a/daemon/execdriver/driver_linux.go +++ b/daemon/execdriver/driver_linux.go @@ -54,6 +54,7 @@ func SetupCgroups(container *configs.Config, c *Command) error { container.Cgroups.CpusetCpus = c.Resources.CpusetCpus container.Cgroups.CpusetMems = c.Resources.CpusetMems container.Cgroups.CpuQuota = c.Resources.CpuQuota + container.Cgroups.BlkioWeight = c.Resources.BlkioWeight container.Cgroups.OomKillDisable = c.Resources.OomKillDisable } diff --git a/daemon/execdriver/lxc/lxc_template.go b/daemon/execdriver/lxc/lxc_template.go index 3d7b2b4999fbe..55c05498c448d 100644 --- a/daemon/execdriver/lxc/lxc_template.go +++ b/daemon/execdriver/lxc/lxc_template.go @@ -118,6 +118,9 @@ lxc.cgroup.cpuset.mems = {{.Resources.CpusetMems}} {{if .Resources.CpuQuota}} lxc.cgroup.cpu.cfs_quota_us = {{.Resources.CpuQuota}} {{end}} +{{if .Resources.BlkioWeight}} +lxc.cgroup.blkio.weight = {{.Resources.BlkioWeight}} +{{end}} {{if .Resources.OomKillDisable}} lxc.cgroup.memory.oom_control = {{.Resources.OomKillDisable}} {{end}} diff --git a/docs/man/docker-create.1.md b/docs/man/docker-create.1.md index d7bdd55789eb1..98d2359ec29f9 100644 --- a/docs/man/docker-create.1.md +++ b/docs/man/docker-create.1.md @@ -8,6 +8,7 @@ docker-create - Create a new container **docker create** [**-a**|**--attach**[=*[]*]] [**--add-host**[=*[]*]] +[**--blkio-weight**[=*[BLKIO-WEIGHT]*]] [**-c**|**--cpu-shares**[=*0*]] [**--cap-add**[=*[]*]] [**--cap-drop**[=*[]*]] @@ -59,6 +60,9 @@ IMAGE [COMMAND] [ARG...] **--add-host**=[] Add a custom host-to-IP mapping (host:ip) +**--blkio-weight**=0 + Block IO weight (relative weight) accepts a weight value between 10 and 1000. + **-c**, **--cpu-shares**=0 CPU shares (relative weight) diff --git a/docs/man/docker-run.1.md b/docs/man/docker-run.1.md index ed331089af67f..c4f70fa389e11 100644 --- a/docs/man/docker-run.1.md +++ b/docs/man/docker-run.1.md @@ -8,6 +8,7 @@ docker-run - Run a command in a new container **docker run** [**-a**|**--attach**[=*[]*]] [**--add-host**[=*[]*]] +[**--blkio-weight**[=*[BLKIO-WEIGHT]*]] [**-c**|**--cpu-shares**[=*0*]] [**--cap-add**[=*[]*]] [**--cap-drop**[=*[]*]] @@ -86,6 +87,9 @@ each of stdin, stdout, and stderr. Add a line to /etc/hosts. The format is hostname:ip. The **--add-host** option can be set multiple times. +**--blkio-weight**=0 + Block IO weight (relative weight) accepts a weight value between 10 and 1000. + **-c**, **--cpu-shares**=0 CPU shares (relative weight) diff --git a/docs/sources/reference/api/docker_remote_api_v1.19.md b/docs/sources/reference/api/docker_remote_api_v1.19.md index 1a321fe736317..2f40cb6fa624e 100644 --- a/docs/sources/reference/api/docker_remote_api_v1.19.md +++ b/docs/sources/reference/api/docker_remote_api_v1.19.md @@ -149,6 +149,7 @@ Create a container "CpuShares": 512, "CpusetCpus": "0,1", "CpusetMems": "0,1", + "BlkioWeight": 300, "OomKillDisable": false, "PortBindings": { "22/tcp": [{ "HostPort": "11022" }] }, "PublishAllPorts": false, @@ -195,6 +196,7 @@ Json Parameters: - **Cpuset** - The same as CpusetCpus, but deprecated, please don't use. - **CpusetCpus** - String value containing the cgroups CpusetCpus to use. - **CpusetMems** - Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. +- **BlkioWeight** - Block IO weight (relative weight) accepts a weight value between 10 and 1000. - **OomKillDisable** - Boolean value, whether to disable OOM Killer for the container or not. - **AttachStdin** - Boolean value, attaches to stdin. - **AttachStdout** - Boolean value, attaches to stdout. @@ -341,6 +343,7 @@ Return low-level information on the container `id` "ExecIDs": null, "HostConfig": { "Binds": null, + "BlkioWeight": 0, "CapAdd": null, "CapDrop": null, "ContainerIDFile": "", diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 5a927395294ea..60c258f889b4f 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -941,6 +941,7 @@ Creates a new container. -a, --attach=[] Attach to STDIN, STDOUT or STDERR --add-host=[] Add a custom host-to-IP mapping (host:ip) + --blkio-weight=0 Block IO weight (relative weight) -c, --cpu-shares=0 CPU shares (relative weight) --cap-add=[] Add Linux capabilities --cap-drop=[] Drop Linux capabilities @@ -1898,6 +1899,7 @@ To remove an image using its digest: -a, --attach=[] Attach to STDIN, STDOUT or STDERR --add-host=[] Add a custom host-to-IP mapping (host:ip) + --blkio-weight=0 Block IO weight (relative weight) -c, --cpu-shares=0 CPU shares (relative weight) --cap-add=[] Add Linux capabilities --cap-drop=[] Drop Linux capabilities diff --git a/docs/sources/reference/run.md b/docs/sources/reference/run.md index 6d9c314dd7303..bcbbe4a9a2d1d 100644 --- a/docs/sources/reference/run.md +++ b/docs/sources/reference/run.md @@ -483,6 +483,7 @@ container: --cpuset-cpus="": CPUs in which to allow execution (0-3, 0,1) --cpuset-mems="": Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems. --cpu-quota=0: Limit the CPU CFS (Completely Fair Scheduler) quota + --blkio-weight=0: Block IO weight (relative weight) accepts a weight value between 10 and 1000. --oom-kill-disable=true|false: Whether to disable OOM Killer for the container or not. ### Memory constraints @@ -654,6 +655,30 @@ Linux Scheduler used by the kernel. Set this value to 50000 to limit the contain to 50% of a CPU resource. For multiple CPUs, adjust the `--cpu-quota` as necessary. For more information, see the [CFS documentation on bandwidth limiting](https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt). +### Block IO bandwidth (Blkio) constraint + +By default, all containers get the same proportion of block IO bandwidth +(blkio). This proportion is 500. To modify this proportion, change the +container's blkio weight relative to the weighting of all other running +containers using the `--blkio-weight` flag. + +The `--blkio-weight` flag can set the weighting to a value between 10 to 1000. +For example, the commands below create two containers with different blkio +weight: + + $ docker run -ti --name c1 --blkio-weight 300 ubuntu:14.04 /bin/bash + $ docker run -ti --name c2 --blkio-weight 600 ubuntu:14.04 /bin/bash + +If you do block IO in the two containers at the same time, by, for example: + + $ time dd if=/mnt/zerofile of=test.out bs=1M count=1024 oflag=direct + +You'll find that the proportion of time is the same as the proportion of blkio +weights of the two containers. + +> **Note:** The blkio weight setting is only available for direct IO. Buffered IO +> is not currently supported. + ## Runtime privilege, Linux capabilities, and LXC configuration --cap-add: Add Linux capabilities diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index cc4c9988ab620..1eee6711eab8d 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -1158,6 +1158,20 @@ func (s *DockerSuite) TestRunWithCpusetMems(c *check.C) { } } +func (s *DockerSuite) TestRunWithBlkioWeight(c *check.C) { + cmd := exec.Command(dockerBinary, "run", "--blkio-weight", "300", "busybox", "true") + if code, err := runCommand(cmd); err != nil || code != 0 { + c.Fatalf("container should run successfully with blkio-weight of 300: %s", err) + } +} + +func (s *DockerSuite) TestRunWithBlkioInvalidWeight(c *check.C) { + cmd := exec.Command(dockerBinary, "run", "--blkio-weight", "5", "busybox", "true") + if _, err := runCommand(cmd); err == nil { + c.Fatalf("run with invalid blkio-weight should failed") + } +} + func (s *DockerSuite) TestRunDeviceNumbers(c *check.C) { cmd := exec.Command(dockerBinary, "run", "busybox", "sh", "-c", "ls -l /dev/null") out, _, err := runCommandWithOutput(cmd) diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index d634b1ffb96e5..5c44de22907f2 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -168,7 +168,8 @@ type HostConfig struct { CpusetCpus string // CpusetCpus 0-2, 0,1 CpusetMems string // CpusetMems 0-2, 0,1 CpuQuota int64 - OomKillDisable bool // Whether to disable OOM Killer or not + BlkioWeight int64 // Block IO weight (relative weight vs. other containers) + OomKillDisable bool // Whether to disable OOM Killer or not Privileged bool PortBindings nat.PortMap Links []string diff --git a/runconfig/parse.go b/runconfig/parse.go index 63eeecc5f6ef7..9d94ce9bf30da 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -65,6 +65,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flCpusetCpus = cmd.String([]string{"#-cpuset", "-cpuset-cpus"}, "", "CPUs in which to allow execution (0-3, 0,1)") flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") flCpuQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS quota") + flBlkioWeight = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container") flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use") @@ -308,6 +309,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe CpusetCpus: *flCpusetCpus, CpusetMems: *flCpusetMems, CpuQuota: *flCpuQuota, + BlkioWeight: *flBlkioWeight, OomKillDisable: *flOomKillDisable, Privileged: *flPrivileged, PortBindings: portBindings, From 6e8cfd63fcbf6d64b475b387cc66689582cc81c1 Mon Sep 17 00:00:00 2001 From: Jiri Popelka Date: Thu, 7 May 2015 10:04:58 +0200 Subject: [PATCH 118/321] Fix firewalld callback. It needs to be called with same args as the one 4 lines above. Signed-off-by: Jiri Popelka --- links/links.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/links/links.go b/links/links.go index 935bff4ae3911..58fec95f56b7b 100644 --- a/links/links.go +++ b/links/links.go @@ -145,7 +145,7 @@ func (l *Link) Enable() error { return err } // call this on Firewalld reload - iptables.OnReloaded(func() { l.toggle("-I", false) }) + iptables.OnReloaded(func() { l.toggle("-A", false) }) l.IsEnabled = true return nil } From 59bfee2fa4f178660fa4cb2a9d18c924e86595a7 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Thu, 7 May 2015 20:38:46 +1000 Subject: [PATCH 119/321] DHE documentation update Signed-off-by: Sven Dowideit --- .../docker-hub-enterprise/adminguide.md | 2 +- .../assets/admin-logs.png | Bin 161230 -> 104525 bytes .../assets/admin-metrics.png | Bin 74062 -> 51440 bytes .../admin-settings-authentication-basic.png | Bin 23600 -> 21283 bytes .../admin-settings-authentication-ldap.png | Bin 25353 -> 22486 bytes .../assets/admin-settings-authentication.png | Bin 13472 -> 12119 bytes .../assets/admin-settings-http-unlicensed.png | Bin 21971 -> 19784 bytes .../assets/admin-settings-http.png | Bin 20618 -> 18574 bytes .../assets/admin-settings-license.png | Bin 14936 -> 13512 bytes .../assets/admin-settings-security.png | Bin 21807 -> 19364 bytes .../assets/admin-settings-storage.png | Bin 41579 -> 33367 bytes .../assets/admin-settings.png | Bin 26041 -> 18998 bytes .../assets/console-pull.png | Bin 35398 -> 31544 bytes .../assets/console-push.png | Bin 49729 -> 34326 bytes .../assets/jenkins-plugins.png | Bin 45860 -> 40960 bytes .../assets/jenkins-ui.png | Bin 42805 -> 30240 bytes .../docker-hub-enterprise/configuration.md | 93 +++++++++--- docs/sources/docker-hub-enterprise/index.md | 6 +- docs/sources/docker-hub-enterprise/install.md | 3 +- .../docker-hub-enterprise/quick-start.md | 28 +++- docs/sources/docker-hub-enterprise/support.md | 4 +- .../docker-hub-enterprise/userguide.md | 140 +++++++++--------- 22 files changed, 172 insertions(+), 104 deletions(-) diff --git a/docs/sources/docker-hub-enterprise/adminguide.md b/docs/sources/docker-hub-enterprise/adminguide.md index d471041675582..66f099df44e6d 100644 --- a/docs/sources/docker-hub-enterprise/adminguide.md +++ b/docs/sources/docker-hub-enterprise/adminguide.md @@ -53,7 +53,7 @@ following information: * Error logs * Crash logs -## Emergency access to the DHE admin web interface +## Emergency access to DHE If your authenticated or public access to the DHE web interface has stopped working, but your DHE admin container is still running, you can add an diff --git a/docs/sources/docker-hub-enterprise/assets/admin-logs.png b/docs/sources/docker-hub-enterprise/assets/admin-logs.png index 76f0d19a80ce96bf2e9995a6076a0c559d7235bf..3221cc54daed5e5ed8ec425b24175396f18c210e 100644 GIT binary patch literal 104525 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 L4@wlD{{24yIVXZH literal 0 HcmV?d00001 diff --git a/docs/sources/docker-hub/hub-images/groups.png b/docs/sources/docker-hub/hub-images/groups.png index 7b4b1b15314c6e455fb06fb03a40e9fbe43ab70a..23dbbfcff43eb481d19a95ad2ffe2410b731db9f 100644 GIT binary patch literal 70648 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 diff --git a/docs/sources/docker-hub/hub-images/hub.png b/docs/sources/docker-hub/hub-images/hub.png index 16840e0547b74b92bda72b678dd85ed51601c56a..489f730f96ec0403f4eb12c834e6b13b42908a06 100644 GIT binary patch literal 68579 zcma&N1z1#X+b;^D0#Xj$NJw`#3JgehNQbn<(2Ynqv@}RbH%K=K455^C*U;S^vzPDx z{oe0aEgSB&4bsjC<2(z&)~yx~wEp#W48}aP!Liow^bdk~cjP(x+e~q+8(8r)?x8 zH%=s^T@xfE;S?k!BB%6bHBq1d-Ra$X7bGMMyuTl0q;KiOz{BTe3bN8jh`;~e+X~~6 zkYCKmNx#+foZD|hb=92pFWmDHqm=OZyfx(QV&!!|Ro%8E80BXlvt@MWKCUGDtw4?j zE&J0O>A-#=E9=iMUwuY@4g@3O&QfG zXOKTyWjP+rSxq0c(FebjM57M&b@GoX{PeHOm*Ilc|9XfI`C9@!1E>Gbf?yiu|5?DT z2foir?+!T1Yf!u>Q8r@x=ZBy|DBi!X82)L7Mj7fYi~qkYUT-dovZ7KE$CnG zg+kPhSy}n>=MNML?QejZz7wJk2?-$} zBs8%(xbk$T(T3Y&NQ%J0= ztgQJfUS9k)u2+^nkqFmk$vkMp*F8TZ41}K@9rcd(qH%~(2Y)?zuFCIxM>x9u%%58s_I%-7=ktHMabR^)U z=IiwCAs<-Csyw%}{;O857#YLHqRZzwCvrf~m6fqK%N`V+Y1Up^lEgC-zbogC9@Mea`zA@LpV@DmMsQj|F zRa7|A@RQncuFiw^(AUkaPR1pnsHkXV#pIp4k&%&)PfLoDYy8;MR8m$J6$J$_`*f}b z;etO)JTX*Z%WANz&`riZ#K{g3qN?(j;8=k8p4}}YQ*+s)RJf@yG}bZZ*bF#6zahqhX!+b}C8xO%>OP!V89&^{hs9fsbVCx8l4IOK z(_X)1_6;vN@H%46zp8|bd9^?d+0Nz3X>J=>FHV3d;0Y0#MD$cv(qYO^|TY$pUKH*LLkFpYgT=Ab?zbx zLDySRT#F!@$L7L@tICSdbF0-3(H#nPgR4$ya!A0%?qp;nW`>3u9hmF9H;Qb!!qDe< zC6KiSUg7z8-xLvn(d*;w&FuJ`n4j=136TbsxaQVAARxrW#g&znQ)UMS6z>uXEb^LeNA}TvgLAtAm8~A*{_2QQCJ*P|0t&)~wKsAi6CVvWXKAW7v zYZaL2X!64!WZVS72u;)1D;Ug(sYKjfd=Qdy@bCOqF2ecz?&e@nNGIK~-?_k(y<)!p ztY|Sc;Lh??!vFSJsD-^S{_31k!_ijCitgi0O(ko&&cKV!si}HD*%*~{Zj^ZcvyFzD zL87HrKdWHIC(1tW!993KM8?V)OzZ|up6m0UCj5TEg2o>X{b+dxj03nqh4?5Za=WEC zzwlU!9aV`qFv7!obfllPA4<5ZYw~oUq1}7meq?QQJL=?5d_f)jnOu1XG(UbSOP%g+ zbv3X0s{^rUg}6JbOs<>{egMUPh!pc%{jwJwA#RgYWvq%u|6uK*ooh`ky4f|k*T}0I zbB)z!dB(p_){7b(Y&*zd+sS7OyD%^i6FGx)a)ndOyKNUWYLwX7Egmi4U0=*zUZSYEC#X#P@I%iC{mKu z%KfBWBMT(63cC303rJTha`3HdaL!vv$UHm5A*~B;RPwepfS?a;|iB1U!}{0z3cjz zm;Cb`=Sc7`4QhDh>8a^2;xLt&$Rg=JDGDz;utj2u1UnacM7K5<=0Ug9JL$ZXr-4Pq zfImca*+q)KfChogv_IY|& z!qCowTug|u%YE4&haAOIKkyRWeJT`h&k=o9a4702{gHG4e!{UC+-`zvAT8MWJ4&A| zsr>#xXSsZ-+7C8085W(mNe7< zeXE!5?%R{Dpk0Z_*Yd|Yia1DXq|N}7wv)4h74x*WT%=LhjG3LiV9r>5fL#0YiNaJ_ zUr6^&pC-QOS4a=T>s$l6O@w9d$3YJEqEP5wQ=_;0rn&g1-QAwo-RwKO#A-+~x9jsv z)tK(L@Yng!wVV~gmyl@H&e@-ku;o~%FE83CC-?|x{EvghL47vCSF~+RgrQ(-XydR# zr~>*OEJ0ZYe;_V<))5Dj#5|AR`1_r}!A?%cf(f(w)}pUYF5=EI_7}R;sNtkL zB?C%PFC?$iiz0Tnw->6fKK;NO6~EX#5cyrmN-4SfjguicuG)E3|GKQKB$Utui4f|A ztDzpUFhI(I3B66^C?qk()s$m$`oS(n6RjfJ)kFJoZ)DK_tYE+14>d#vDi(0tr6PJG zO^bSBkPzjRzTgm+`>BSIFd6L#ROdIu#%8RN#YPZNH6)BlRVPAQQrYa|9j+iR`wgm~ z(v@khJMx-^i46}GqnMz>oR^nBj5dyDX+|e*nhBnBN?a(Ju0lu5rraoKzUQ&8ZKeG> z-wY2Y%PZm=&nmnHn?kF1Cah-H%3jhr{F?M-j>CRojm-;~CZnaAT$q8VF#Bp9vOVm= z*x*6rtO~01PP4g=-}wG}($Rau2&ZSUXJw1c8uy%&6AP>=Xudj=ONR$UuiC$H)bv`! zB@0$oiePkC7GrXz>x;k+KTFZAz0YYQd^7&A* zxK(SY;1rW~G^X|kS=s|+)YT~_I49OgN<6`;Jc4kI%RoZ|1vI^LavND+b2f9Muso=L z+?G1|%xXHQa6}GNSzNm8wm;E8kJ0|ku6^g{XxwR5NplWfT$26lO%M6_ks^`9LO`BU zK4Hx^>_aJ*en+}F|1TJ>hQi%uL&s*d|MF^lHd_t?WdPQc4pHK)$IXiRTU!QIa$jR*r_fJer4Cp<5{``_j zQ`XwLC@qal(J-=)f(DDsLc7V5ii+x8{Usq6nFI9=LHE+q(wYIRE%O#Nq|Y{{vbbF0 z$g{LP+eQ=GXiXd4mswQwXnV_QT|1^=2FF{c`Dz&XzScSFdhX8*V}NCk&I~~)prGjd zjm2#Oj6|ma@sn^N#a3RPrF{FAH3Yfnpxh@DYK5j95F?ynKb6Q~7ydL2|AylwpVu4{ zC$FM75n%8#TMqjtvJ{A)UEryx(v3=@kc*nK__Oq(JjXHQBG|Is6|HHLjoI1^Soe2y z4ZjAI4nbYyEeA=o4pX%}CJ18i!Ip!pJ$3N&C`a^xA@V*ZEbQ0QJql540L*8Dy$fLI(_NG%447zCZSp^}^Z)NE`@g-{)BZF$hN11-Dk6ky2(EKeYnPS%XNOgkzX{-EO) zulkXPPl)}HNYm~+Pyya+D6p`!*w~!iUvM;Q)1H9!73L|Y2pxSMA6od_)!LOK5|hc_ zP@nS@Ez7-W58W|T&IdI#K}&mw2$Gg@=T2GE<37Mr`U3^McvoBh0xN2Us)c2`YO^&5 z?pJR$sN=Z5JI_9K))e~-#c_GKg8gPYR#9a2+^0ZaMSM!fc!!wyOrgeJL&boqhvspW z?S47ywAk8)>J-IGg)AaEI@E~voEi?8hQeFuFJ7FUpG%`_X}Ym&=&!LUiqTZ0V>OJQ zZPDIde8Y)Vu~-*0xO#bH&0N||C5mx;e9E8qdI4%n8@-iveWEu(B+v1SgOytqMkRJd z7)5?bE_4algxMtp&v7(ob^)_2CSYe3pJ8KY&%DcR_PUG4hm?^&uT-4#TCf9; z;|JHyFW#BGw|Ne4x?U{vA}z(WH-WjRO})$Supi+14n-OKgu2U%=W;T<04Qq-vvI8q zn#@~nsU*cEV;GnW(xe>451*r?4-nuqwk*d#$v}-^^n3<>JOt**mA*FX%?^UN~TZWS>~a+`{iu#)7H1S zb$prul}s#Cj<{Swx!RA40?vorKx3gF1WB#l@UwRu?uv7RMX9OZ7D8E;x$uVBN_!L8 z76FISA`4EcGImZ~W8V z{^)-9kA)b!|E$C=k>xx+rIR(+iW~P76p_IH40E7Es;w}};=>1ncHgGl+}zeyauf>- zi@7sTPtUx&hS6M3T6r26t59^_=wc2e&o!WV2@*EiL+YaC8}ixuVQpKNVscXsSMUN0|7RAmll zjGoH?`J}|B6?R-akyf?!Fffa|yt#oAskN4&ixJoptb@7oY<>e461am#X=sQz3>mQ^ zz%dqM*lnhH+IQT*J30ydZyxq@9JDrq=9yR-Yoy-wrs!7{88u#t!ukAmMf>=#ATw7n^zg>;KA-7MjD5UEy>@$y*Q{S zFCQ8n4i%8_I+a%J`IMHX4`2=&8XCYZ`S`JH+st6XC1N?!$yZDd?$4BHF2g3c!I=JDRo3-j6fyKR+1Jm}O#V{^_gT>A= zF?rBsMbiDl>KprYOF#w^3a2z|gX7w)MO9#w`(n90^<$f9P1vNmZx6C9^tw_am2ugz z`?2*`JHgRe*fjFSov&xYnKWk&HN)$EHDLfty^dQuS1y5wi6xg`+jCuXBIDlXrY{9m8Rq?(E`9 zyB@V1{mti2L=MyU`+ci<63!XzdijK9-i63`&+om)8a!GT*p901#*CFAGh9+eQ{>Jd z3IEN7`Op;wh#PDK|L7H5m?QPtP_5O8#w{Q?;;-WACHYBBd-ave_n=_j8U#r}T38wK z;g&%3VV)4g>*?#8o0+*zL2(%2zk8QO{A(v^;nR+Lc#sQ=-@}1r9J93#cIIaZ?etb# z84ir#g}oaLCit%2eYkwti%T5ZjtA8YJO>eH$(ClrPZw|YF0!OD*!?fPh*qTHhw51>!YdjEhsuw``T3cJwGco}9$#Jpy*=!=h;Tgl5?TGpn0AFcu zZ|@3AOG{hb7I0W_0wAR5=*!E?Llqb@Vc-Yj4u8YkyRJuRQE_ofmvB=x7LH}A9+^I= zo~Dwv+@e|oP2=-ig0*3%|Al8+)qe0Om5& zT^EW^G&!C^8=C4gNq;X=^?1;2XsFw~$0}3A(A$glG`=IiJFTXC&B*H8d8&oDpVjdU z?PUIu{pig(@e=asOO|JAShV=7?|bzJFc<7Qbe$i%_3Dp=tXUC&!N7@r*<`tgJ^Fmj zZJ8w{MdQ+`!6ZcK!+u(W`>SGl?SYj7M(Pzj2F?d_1R}N->ZF~nf{!!hu*l@$`BNd( z$*skQ$|CbGu;JLkp-32i|5@_Y5OIu@|NX*N2H3#WSa1kNUfMO;N$>^B>VpLtygfO? zK`}*CYrAoq3D@UzC~_7p=KUtdhLIcy2Rq_TEUpeJE%*!n+#=DX2Sm0ETDm8zS;8_b z;N%mYDaJrDB<9&wUzQ_j#N>7rlI2g`%#lzx(9Hk`90M}K?ejhC((E|vgG7Y?OftP2G~;Lb_o!de2?`Qty+q<7CniowO)cKF z<6_+F0{aE4g0?;N^z`1m`SizO6cX+W(Xc|reKN}XT9Es;&|uL!s@g{0^cNWxo|49s z?mPQ$Ii2C7%eS44g@~E1;A$vGA$xpjOD&AL@!rfzBzG#UqMuqJy8c@COJ71 zAS}3o6Kc-^xCe1Ke*}pSA71;5?|%OL1lm|ONtg&f-ue3VC1$Jdje7lCt;Zfe4Gla^ zeO|j*ELc9&yz%>+WsS<*+_OFx?D$O%UcHZg6OZsryh6Taj(f|@=tB%zIPS_ep4HIb z1&g24{A-zD{>Q#P(5NX^?mC|F&B31A-!&9BqwEu@1Dx}d~rGnSn@BZZ@NG+{=NDjMHSyC zhcfX&LK`u7TV1e`!FE4F{#iaP^or=|+>Gw)hVR6LnejUP3Mv4nLHybC^;JN?<3%*e zmll~zL|&vl$noRH$ZaXj@Q8M_?9G#t695wW>S#c5km<`=4bUac&BD~w)SR50!oqs| z@*q31398%3*Lw3J0xe=TA2s~0m2fJdMEoU>e}-kxB)07R9?#8I@bV)8V@foFB@C=j=fe@FT#!))#$42 zV^U@&yIwd3MxXriJEE%(_)x*di|T!q{fE}GQVi1hugv-)&C5ocmxq}hjkx#2ZmY%% z;l&OQFU!ZR|INQhtEsEgq$Rv5URzrO1Z_`VW+tTtfQT#-g$MTb_BJ*Ova)DDA=0^g zs|;{ULFQ^rh@GyZdh{J>t6N39>hBh3T*4;F6w#l=YSA&CWI z=`duK@Uc$TRZok@KJs`B;KWEq0Dm<&B;;-G+39JX%s~Ae@%Zd)@wxL&;Z?O&cWo^% zqo8K{tvP}V59~V=R%v~YTKGdk_@L$Ym4)KX$fKKRb=hNp5f+JRXFwwnm8PZ?8ExzN zUyiMRWF!U(-QM1Q`M4r#ho0a$g6#&bIBuH-MafgsjF)e#hC%j`|04P#6~iMUz^|`Fpq>CjqNWBKS@i@# zew8*SVT6Sg#KcKc+;HGWbs!?zs0MuwyMx=Zg<_nUOZDa@ZiPNaaYfC;L_Y%@UB4_Z z-;PS7mp>!?Uu571U~W}m=>L;z{C8RK|3O0je-`}T*~!y*=+-(RC)a=e{8nJ3|K>Ku zWugRCkpF-7;s4&$KNKQBj{ffq>HkrX{l8PA|1}l=Dfst@kE8%%PenyVfJ9{_e62YK zA2jld&BTmUEez*5aH6P?(B3=PpDm!CARZ)t9%Yif*7#RsO8N~Kt>18l32Ohm9HMSwk)`3emJAhrJRp4}*5DV*2C zB6LhpbQT=~7IN`u4mWR9qj&aq=X{T4BNwcELIEay`XwYv-gryoip#k-XCrqzni3n3 z_B6xNe`(ZkK|#L_;mijsij1CV=!~bE*m(g3=~bGsb>qdQ*zz~lYqRL$)S>6P%Cdbb zhta02b!K}GQHe*_ql9Y%%%w1$YZ8FAajUP*aUKa9*I*^gCH|U_Y5E;ns(_6yM{Hx( zCSz`?<3ji)++=rl!%9PJ&Lsw4y;ZN!? zm(x?OR?}tE0~7(nI_y$=Yg_LC(3dgv2&|YW<9Z8*;laEc3=hg0wo%(94Nbk!h>&7q zva~f}nH~mPGN+H~zhi5Nwki#-hFYH)4|DMtw~hW<6PK5De$HA7SrPo@%&h$F;B}vn zg3juKnN;ITw0e&jEEI5STdsK*rg~d*5&Mt**o?>4fkbAJr7ChY%(<__2KLFZ z{5l`??Po&2c1M|is54u8r0t9yb$)I!jxQE#-e14&(D%ADH-L8RT^xb?spNBWQnu@ zBWQwaHe^*HJZsn<15X48r*p1l32N8I$02&)SBZz0BV)AgtpBlyq!W{dXUTFLsBLwy8& zaEZ*8^PnEwL9e~ewa&v{6L4!)v6pJL*@~Q$sq<}@MG9VFK1cd6eESD2iu_+;ecq&F-ZI52oZp&u3Y`IR}My>~VwFaMS;m;D^l_?E4okp1v?l>K9%n7OA zi3AS_97^OQQ92rLOiVOVG`k2_>!IP0fV9X2Me>ULsn+X2#Bj(lYJsgDGl$@Y>7}2Q4O< zKk1_B`7Do8QB*NHLr$)2z9F1e@XEyGnDLU)xYpj@u6s$6vDDTUwT|q0cw_vRW}6rM zTr;X%$9bm2<_=3|apvmW+z>)~`Ir=Vw`Oh8h5BH<_ti;6E)tjH@)eq}sYCP>h zRL6A0)xhQ!FLGtc{a79lO6@Y&N7S1nKtpf3F2xSl9??Kt>TNqZ$_UdDP@^JXY`eL{ z2Q6XO9SuHj0Z|)Y#RUJkxL4&F?kMJKU>@fwX_umEDmNQU(kr9Qvh)_lkjBQH$3qyE z%;!PXfX87cS&%6C2awIQ?>mm1{LJII^Q|_{wbx(C=}BiaB4L!Vy}lw!zdS@ zy+Gkz%C_TxhQ&WGAqUBP0Y7we?%R5Ps&cL8Xx0_OfPbo zGLArJw2_5Zq%ym_I2S|qWSRXbR4%~j_m-!tk(YvxtcQv(crCBNPDXDlD|Y@*=t*KZ zg^^Gqsgj1#Btnx!b9{=afZaDKPHTE>c02;BTq8jjFwd*Zr!^Ex=7l8{b~%sf8swE9 z^2=4!;$M7G1eDQ4gy24^e;7m6X`e@D&V@dgQA;3eE9x3pW3!WAiR(i$Z!-t}FS837 zi(LDcv(viy9kt%m{?yULnaw700rTp04FPl0k#DUeW2Kr9b7Gxu->-{SqMlW6KC{w^ zM301e>yvv2S9(y2QRsm>;3YRp2Y_c~5#?y@TGl*;fD=S{!P=Ry8r!o^^18JW$a#lH zqH`H8A{uqI0?ZzLM|O zd+OpUscxq~F9K~0oX2fB*!Gw0fRcIQK3{N=1W{pw1pLx5^B?{6v)<$2QWpnY*JFF` zK3rF18j*D*BbFy$dRj^OB-h%D#oywc0H!>~Y|nI9!xCCUfN8no+aU%AHC^;eJY__( z&gT%Q$JM2g5>sd+qK~JYky3P^lrVxk>UR&NnZ(}{wA1*+8gQhqIUM?G-P}~JA`w*B zfq?8XKDbhEiE5OIy7ureSDWZ_N6c}#rmE=qc#M25WKxHjZVmX2!LUtmsw4uuxl|Q1 zdwhl# zUrXEkMEm5$GG)?oJSxj^Z~8@57;?(@P0Tu?@mlR!+8-|munz%>hg1xdg~jhk;^J|k zf8B?tlpBoaD?z-o$3>>B>ZHq?nD{)#Wkrs}8ZS$ock@#`a{90n#+}2v zA*>3~qtUd)fL+38vk!Pa5<)zAzAC!WFCkhwBLU)fmbmF;1Mo8(MDUPuX8aXYc zExZ?8CO544dGDW1PoGs6ghJPf9IX-tDuEL{R+5$vMJ1SIo(__mVd2`3eeW+&Dmij< zpJtbB^nL>|y^~dE6ZpqxOZ#!8-3$es{#ET2{MHWE6A{U2J~|!VE1EM|>0ZecL;%66 z@o0B3jz*6N8^4KYjYGDB&1*6bBB3ANc(tHmg&M#*dr@Rf8@`TSCul12t@|~~oe|l# z1~GPWnZoEDdyrugg zTlJ>A#NSD5b6@rs1PFeosmnQQS=!bt*Hh?7@9rf&VypW}FHd@#E z^%BFBXctB^d79}Yl8*CnHY6bpLYpJLLrWEGT++MW4NhVudJ&HJ~| z(XtHSS5&bMJ8NiwzsZu+_sX}x4}e|hMVS&^3`E? zX?cZF*E*ED_Zqd&zRXQEc-{STziX^pPOw^d%k2?(+T89^~ng`YZrR}l&oyhPq|4{vI=lYr(@TggnM zeJj_)DUPGnIyl(7EV|Qg1z^f}>SY`$_iiUpz{BP1(bsA9e^)^i@IP7Bc-UC<7vsai z@}X4)n@Q>SS&P|*i;FoUvN!P|p?@7q6o&z)|JLrcN*XH}uZKzuvuD+(V$BA~0L%&Z zKqcYG!AOo>n_p?iF^&=>`+}_)k3-zwU%{~B)qI2JF}@8CFVGKEM0ItE;0KhN=9N=jivgU-Y&NiV># zU0H#*FW)pA#y6z7pF8J36S9E`TNuko8_1;d|R)5|X!ZlF89~^53ZFEnbSx&RLq<_sQB)Vx>Rm&5BQw;<)lty(dli_@(4ZO{B?8&nb9X)K`6=Ptko`O;J#HfO#FPO zM+hl+d4?0*?O_gd!o#MXDL{;q?(cf$Zx8@udLeQoLVg!kqg2&>I;l?{?b${g_ysH5 zz_!^oW1HTfLRt!T7m<$Kz{mLCwr?$s_=Uf(2e3QirSp^mQ+xap+s8%M$V8g`eRyzb zC0t@0cCUA$6vg7tBN}6MB#-u{5qsV#s;gZx1d%s@Kt!b3(LKFtX{nNgrHQooVc7Z~ z;6GMY#4=4PahlIMKNlApXIXXhf2`hI_!0)F7Q_uQlB4xabXGA8w4D4LWg?8AHvQ|(R+(YDqHk>`G`fG_Q5P{$V=3_#5d~VVgy0wytCOZ5yo4! z;5DedhLa|JaS0f3Tm)y|q4{SVY!G}-|8&BH&nx4M{42P2WSs8ikH6g-Uyvfo!n$E0 zQpY9`^0JfDNi>|JpC%T=4*d?8@-Q$3Pr7=2I0>gFri6_aUmc@Mjv#{(CODfBIL6B0 z(G9?D)4c)Qx+>}fDJN916D-wB^df5b19qU!(N$Ib3V`HfWMq_-lmr9>L_`4F{VgjiM$T6)LlxG? znPczj?jW8K2LO10%e!QJn_oUs$^P`7I8kRI?>0z79RGy+dtqTA5X;KV%`GS>2mt#) z==3R@8L@h_oT;fPkTr4t{wq)wE~jI1OD4Q-nnGf3j0q4qsxT(F_n5JPTEk>uNWdCs zpreuMNl9rb0JTd>N}9}%H3fbC%=vsXO2wBzBR409Q_8@=KnV+>5(w0WmUD3iz-@qN za&>>9T$l&osURy-YMZh!&$n;irVxTaASVaY_0iE$Vl*tQ9#2dt3FoJ0sZ9SM%i)3m zu>R-#^3u}MlKDZTH3ndR)F`4&iJOYBiGUzOuE>+?@SaqT|Hs=-))OHSwvMkzv+@cG zHrLlpGHYvVgM*(&!stfbpAoXRlK(LJ2hdi9r82^UzBf1PT`2*r)OtCkO?rl;AjO9sxE;GYPg`v6CjOal@RidSSf)L@9l}Q{BoqxhI$QU>pP@uVL|^PcFJG$a>$8;n z4AC7h>h5`?qoZSJn6WJnHlK})`-@cm&Fvx4207Z;qHQO~ zEUB-bHrATGMv7PoA^{R(q?eg~TL4QGy4&F|(*gx(?C;7`xy~;R+Pa^o551;i=M+o+ z3x9v0Lt#@Z*8CM>5#T(PEH8|WS0Msay&^_t=KJ9<0fjjY_V;g>A!uZEV(cR#Bn)TQ z;3e#{UA7M}+8}CL4A@;+Uv)pSaW~2ig%TxdFZk^H{JI(5AoHNQdkRFEGA8#-Tlr^4 zS7c%jdl(#vg()fG0nlYhYvzq{n->+vQtO!@tMJ>%D~zz_gA0SEP}C6=$__ODwLfBF zATBv}%9tNK(GHoJD)-E#b{m0xg{?4p&Yk8K9cxX=>G^mKT?6;Wx$^rQ_5ELoWLHl3 zvAN8rQ5!$2fQ+`0i32{YnaUgW>{MutCbu64&cR!w} z^Uv&{&J8;yPUW)qD{yOG*=g6syy|XiSKR)Bq~~#B+7)S<9&BQFu6cmnV^J*2;=>j} zvR0l&`->MbJ)Vgr6jP5A0(n33Fo7u9UpQfr0@5&Avv`v+8xkQ4g2`v#{LvF8@aqld zw~U^e2Hd@Qmg@#?Z7~-tU~+dLQmm!837lJW46*rtiI}oE0>FJ>zpTCi3kwUIm`_el zZftDi`z3s>ifDv2PBp?C4;ycf5JqrW!V`eS%AhfN|DFkK0C=V3?}2Uj=%;~Fc1hvB z*;#|MUALfW;l`Gy9e(}5&<-reI*Fr$jhoXY8d?O%n93VfGC)M6g?9?wuTOrL>|3os zp)pJ>>-O%cj5WvWOJ*iEh3L1PNaiKCy8|Im*7NfNDbJtl7(*T-iyx7=SWDpUe zrTA$n#eSx%tE;4`hLI(Uthe|D5lw}9ow==RNV8W-VSQ;Kecp#{0~fH?kjC=M zZNbf^jf>Xrje{>ieOjR|EsH+k`RvP$WW<#7qaKou_nBgDn-9D}yqOx@1TPEj#(nDoTs z<;k=kAN&+cp_6T7E2tP0GhO36sG+U)FvHN_`75{h{bDt_!@=Q=qR>dj7jbpiYqoNG zU}GL%|7^G997QiC%5VSzQ>XcOnd3_;*}Z_6+_9b>vUEiC*+)OSdDU_NcKw@t7=HZ| z29U81DxZUbPS8sUy8{CQQ&Ur!6L;%!*jq~#7N6+fkJ7t}vazwfdiBP-VOtQ~-bg}4 zg}@d$TY3L!3s2&vt|_13cd0e)Vc|zwYzcQ@~q^MUjl zl?v6#CG7brLI^VE@Ej{JuV&|87XOS1O^6C7SJys{V)Z&=IzGA!$S{ZegM%N(2&dr zu4Lj0vpJxkz)~9edJ2HbwpZWc(u~tDO5T)CCwN=e&bJkE)Htus+N4$n=WeYNh@W8$ z^$&N$y+U5=E{slU)E+xnf9!qHor4F2A$)7T1*G1}27vusa-kRN=GC@xbMc{!#Z;Qb z1-wfLI?Fgtf!%lErg+%OU?<@o1x3?<0v2^R+Gh{s%+qYGbrk-U6Nv`)HF@#k#VdHV zw!UU~ccGM|B@d3}59%~g2HahUe)+xNMg_Yogov4hFGbq`t&Vw$N=e0Kd|j!RzY zr`$fO4}YVLB`&Z6Ga~$9YjI;e{acTwfP^z0n_g(;L`g7YkJDG(KfJ*_Gtd3%V?lN@ z1FIFm@smDUbGXXtW$4gsC;S?h={LV8G==Wbv2i0xLtmVm>oO5`&H45}T_hzVqrH8Z z2+!e=Eb{X8_0`fMSZ^sURk&-@jQr^6SXoh_{O%nZCT3)GbW)T$Y;NfW$M4^q?gs;? zAf5iX31i966B1I=Z%Ij06BBe++m1*R)6;-;6Ojb(>Z*#PzK`(vHznS1j2ndyrj}Ne<{%p6liWZM{ z)fjg?AawJS2PE2(#mEZs^WS!6p8}$|wFv*@$^gZzvb9>c@;h;d=X*(dP4Oy6FCgcH&I7dXL;u0EFg|% zUF!3()XU9Hf_5vT5l{L^D@hqk(HK?`Dv+d?6L&`;-P+R9Qd9)(e7hv^N^P(Qt9SgJ z%o8;5Q@XWO&PwPu2js8#hUw)m(yUWv3 z6t9fnj}{%cn{%oP?H?o9MEpiR?9psK-bU?95N2M2yys-%QzB|N5as|bT$ zPx`?upg?hR1Rv5BnR%iZ`Z`%r z`K1XfN(n!CG~#VQc6y>j7Xl6}RBsBDAwTb64v|~c!wt)8a9oiUy7w_s0f_21puZFJ zx84W8XY-u=#=G!c>h}7&;=yul!&bOxW7n-n7)CW_KBt<2jHFSPzHUijMZU*1O5BMe zBV2x^W!hjtsykOTGY`rWK#Bf$eZy$PVOLt3iudaz^zUaYIemXWj+yOUPyn?2Yl{{f z@-%U)PFB29zb`7%wEC)f4fEv|6w$7V& z%vL(5YT%%=*)y;2pcl`APIlfA|J0Fs7*hA;*R(J;F=4OuE0HaX0fw)#e1VWDX+fsA4@F+f)*7_h~_L|xYU6Py>5O*+En zMyf_?O1)e9`9Q)LA=$qu#vPv+`ks#<8WPVKj2Jf6wRn6@l+FZxAWv zv!Fm^z}p<=(|3LxySV?H^SKL7%P`FPz6mrdyt&Y5zZ{rb5AMk)NYvtjg#BXL5w?5j z*t`tB3>T$nvbUD5js2DLX@xU&n%1t@lM&5%W*}PN2V2>Tz2Lf^CV7poJY>X;)b%pc zbeyhdZ1#gZlNxBcEcOgfP-RcITR9 zZ*trR^`zSg98??C5M-yFF*}Z5K13u{5_GIXO?p#yzv)E=w$QSIHBdA7;Pe?k!P84#GxP9l)K$SJ;PBGuLzpEXtWp z{lLTkBJZC~J2_QQt^4upK+_xzwY_<Q+~xSUsKLye>w(JNO$E2dgyqrEn?+Z=kMAb7O^8do zIIAzb2nQ%d{fAo<;ecdbdk-_6FE6*RW_>}F?Dk7GItz%y{+1L7Wn*Rst?liSeV^#e zj^$1_#mkKk zYK754dwXQW!T1pR0ZPkT2*Vh1>!&RK5ci%M*2#eDUA&w;XoQJ^y9 z*8sfmI+0cJ`#P$?_U3SQIUUIT2TnyNrwM2O_qv`9t<2lfqMJW&)-UWG z$!gIdYW(C20Wj_Gh#s@F_(F~Z9Zfekw~Nb5Nmc2gsN=*P7oysRhVH89OwobJe^$f* zrc<%V+|k_VUAEZzzbJd_uqeANeE3yVR0JGSS_YMFkp^j`L%O7qF3AB@1cnwVX$2|i z?huAfX{2=MX6WX(2Yt_Z&pF@s{jTr2_yc%io@ej1*IxI%?zKk8yKZi&*yiBZx%g>@ zD8iHt3qri9eFvoN9TR#w56B;!wVb|?&^`5*03sy0SqVf0D_I;9X`%K;b@0i68X3Ei zl4eM4@y2>V0PtAC-5kVaOvfz}r%_Zy5N31DO6$}yxp|}x7;P6L1O5jU#pEhFhMRr*mA93%zJBfKVbrX7Ir@!i zovpa6Y`tdrQJ>do(F-e`gmyW@2M4vu_uJYM!BkW(?rHKD5G%sxbGT_z)>l6wZ|#e~ zv4%`y0*8}M!r3N=CX0dNF=s777ryRI0GnQ{?VP9;+cA|-f>Kf>ygt-uIG7xJ=a~Hlnoqj`>OPQg zL|43!_nXw)F#n?YTQxg(4=Uf8)aO3?{hH_1n+G1!fxrvVOJ>B#jy7ui;OAqbJW8t? zW|(Vo>W(2VDYLWo*x3E)_qsPiFudGjM@9dZn-=gQqtg&C@HNKUX(Y%dgnv)VQ>A&Cta!QIA6p@SW?= ztt#OS%k}nQ8yi@!f_S1xT?4+?`gLw#VZV$|rCsRiWVEO1*uZkYxN#$(VLYbl>(42(U#h4zQ{`qBvA)oC9)jkyW|!Vsv@sIZ+~T-4P@ zt*708_OtVwWfKsC*h(OrFBOzmMygs_t#Xt&KCzyvnKa`hGgStppd10}`x~$^SEz>O zjar-TQUAFyL=$_7B!a*H1Y$S@ zP{X}Lg18U_hY%`Ws5k%PUdxI4&2D1FL>oREluUvztw0l!9!P2&E~7^fs{knw0g6%a zOgK(x)NyAc7#Egk@a{*oJIjx9yV0r~0+~l3s6+=OAp8tEWDBY+>d6>q9CBEuI@2}~Rvyp%i3R)p0ykK~X+(uP8jIKs5J4LvhT6BFh6!7^A6todi$Lc8Y zWR2g^1r(`pB3!YgSigGpg^wzCa$!I=W1FVUOarVBGM;h$J4~3tbSgZvA->|L&Y(m9 z`IHJNpP;cy&xqZHQP|O>8rg>AqYhK{frX#9GKoKzsOPryyeRjc*csC0*-Z`}ZQwKr zX{ku8ey=peU>25V{o|z%p_7qdBy)`RCjUx;a+bA%uYApQiGoR=I|;4UCoH@^oIZwc zo7#&QLdLz$N#{R8ZSSuhP!PHAPSN&Bc~cGzO*$T~`LQl!r7!|n+!~KZanMvNh>ZLJ z*c;}r_PozKRo_NuB!HHg*3OjfXHC6}+p(S!@ZDjUmPeQ~mA$#v9C)Db+@t~P)hu3l z8+*|6y8U{B%eN0{U8G;&ih3qC)+MNwlBY|D-RU`*%=)(iXY5az%vyzDW+sk@@d+P! za|=C6){m_Ed^aQ>&TNHiRJlm*aV3`-1(Bzo7hqVZPHaBVYu^%I5MIJORYI+%E<6FbJPIt((@?QVN?wU~v zdx2C{Sud!%FSvt?XADwEEqU(_O$gYNrNZC_&JKh{ow*+_kyU+-{VF9STa_u+PQccO z8|t70t-tsXoM>5W#@Q03=Ohs(HL!{lPjBcea=$9kwvkl zyXgIB)b(kN;*59JKuS-u82v_PZF8)Tk1rVIqLI(&+GkNzc29Ut!lHG@9#N9~8lgs1a^(N6p<|ph z!Q>{;uUOo3QMaAmn{YK})#7y+fn6KGpZV(yny=bZ6xhW*MGW-Dr}&&NY&;c{vk#AK zMCp*tj7ScN=#hlH8UI}MAssR(QWPff!*Qi4F?uz6$A@?HG;5IAl+1yb{y_s?F#m~v z2Ycq^F!$GcE?7b8GezdCGvP9*XMG1fbct2YT>F^~@>rWG)$JacXM}f*zX699UR|L8 zidyccX_O1QFU?}6KgF^2>_C;N4@Q~lxC9mSVR&0As;H^*8~PVztyo!Gw%*H}IIwov zNGP)4)xGktkBTYAdd!vQ0fQtGzyL%_JUB0=5{0%07?eaM); zmtkr;OZi=Yc-OBhNTv%RklY7zZe@Fd!hzbh6P@t8bZ@852znNB0P!M@=_f=P;>b~E@bYAzP;(oW5z4CqJV*X54c8+XUcl;DF zwRGy-_|%l3d0gnd68ou`#x3cY_k~qki=Q4 z2m?X_qk#nJWG*UE8kGbY)1T-Oo0ku|-qOv($KHC>>m|9C1z$YBszQ&vVi*BL5RRgA z9XG#V+hNQ)(cp`Nuq3SJB}k3hX)ng>RCx*pDM)*)P*s914^bgHg3^58?@eNQ#oh-Wd1>Tp_?435~4ZSQYeLM#<43| z(fl5!Nvu+ETPA$9Z)x6_{N-?1T_468-M&>-C^CR|N=Ti2?-S?naR+ zhw>RD*KLDb#xRU|oXO-`KeC%WBYM^?B4n7sVYWLiD5Qq9~di$-a3X?lM?!pxQ*a`8gly0=vIB}!_1(9o3w~z$?KeWsiemD zA0l&VMrU&;1=dq^OFiY63qmU|eT0anT;j)4mje-YGoTAuA?#(#H_>_f{cfKJdfe&u zNT5jWP2s!7AU^_%-iT--Xvyf1x3ZTV=iy?KHziJK;D@CxjKnrmSQsuStcL9&pXMUY zn^M_7`khT;Jz4oA2u3|J=f8#gng=Glz9|^@!URcrFG*=|fh3a83-z@ig!m2}GL=E^ z3~wR~!x`zo_}h>ljaXF^+E^N_u5it_j|ba*t)LQa*3f)=7-ZgHO*Akzv<6!gis1tC zcekSzuvSgHPID@%e0*#W^Wlf7<%-7KlpZIRRAw@g8NCcHrXLzsKCs_sDNfcTI^8w! zqGohNg`R}Bn{qlj&RcK2tYScvBx1f{*73bbBFe62jPDdPo>;Xq)IaoC6T)pDd`3}I zeY>PWBPw6{u0)jv<#!;pzL%c{P2RiqMFrtYgN^54T341!AiTO{{RuwfGqENU^Wqk; zz+fJMdkcKV**yqevzz@@jNGS7FddhLP-o8jc($R>KveSzMs)b?2b^})mAra|uf>t% zi4E9XLng<}BB`Nac-J5Oic}D=mC*H(WaTkqtR zV}lE(%M8rvd+i~Zzlu;3;mNOa>6)#3HZYo*k)&-bmF^dBtOI;&KC%l}BefKt8ilwU z6y$bh&>u-WByiw}k$|nG_mnR-fUR}5K{T#48ogA~ZGvTN&!9MR8g(y4GBo!DRh*l{juG(RO@ZR%@KK+;c57hDVW!QMmHBFE8`3-jG17LOHCis(7G0nLz7~? zFS1KWgA?+#F}0L?L9t0{{b%Lwj19Sdgo>x&Ad}CE98j?GCr!LPSRGdzA3KnLm!L^n z-*QEQoIBo8tKN(|2nqI4Gtg1yct@y!Y5Ix!cX}kLVMabp!{S43OX&kY-lOY$2{(&R z084R6TQZTH0ct>R-D~%YE@83t{7l9iAz@ddUOfiGv9@xMo0@!{^>xMpAm5WHtB{;Z z<^afz<*dU(UT?}hlWZY}J2eY*3{p*N8DXs0d{ynG_~el?OL8m>NAWk_7F`nZZl%Vu>T9r@uE!{|!r zS!~-U;?61!;Wi1z2;$KMD&=CkR~(n`ckJh%kf(hC!WFq3LSj8#2(yPcx!*une#PC3 z_7{*_V2fweF#Sn&JjC_*g&5X|brXxB;Mm5-tjnnw)xX~xR62@V*TV3|44g(D&z8n^ zRjf6Y*K?Qfy)ZXW)d;_W4Z)=TUIfC?2tF2Z-Ma*kIv{ca(G!3r9OKpyN>NP3lpX9_ zn`@+ZU1G;XvpD#6$7#maUWZw;G6+a0u;MD z|L*R7dG>P@T;-ka z`<-NBf$i^D7ZC4R1A7;sIavcGh-qo~nW>ezAkz%EgUg!(7QZ(5V1K_?&#dId`U22> zs)B`&egm|M38;OjEC3>z1JZU7vMGKvOiW5BaH0h5v8cbT?o)Dz!zWyu&@uxidulx?vEz&*Hj}8wPe*LTWNy~$C$~9GghgH z-sHV7BOrVKe0}}QX6ih?YdX?A?1urOdytp~m|7n!I#}=6GXE%k3Uh^&bl2MJC`NBA z@w<_gHL_KYHRPpgSxVK)JH2@8a*NqFX{OO?j&0@SeE#x%IzdMRY^9rZmP_vCrPwU??Xz(I#(pJ(P7$3hyd;ZPnnUhsc z@(~wMZ!obiwtZ>(zyW~XhJN@i{Lm_o6m?#X0|7imwt)p`wz?=rC7iY2dL~u)IcDSu zC<;{2a~vvpwN8qfYFmG@#vd~EI@`dd&a}wGSWywTRqX5_vCubTl>4z!8QSdrSO|K? zVRsv&g}FHb;h}9?_zjJA@zcUd*?2CJN z1=cRbpu!SVYn~(+NasmkF|_0igUc3J;yBSbYKshOTVq5KAJmSHe)?}oSxtUgs#tn z+{9GzcMtdn{Q%&Yc#d9xpd-?m&DrOQO|(K(A@{VbdX!-JdS(I$gJCw-hNWZ2uamqq z3}ll*oO$#3qHo&0QO#lA6Q)z#VOGMdQ!IE=-Z14gF}+d$fNYR^ij+)69Fb4Qa8*V8 z8X@6b!j`Kl={KdN?nXc*u4G>w$dM+#{^i))zMH3tm_;M)#@4}EcR*jAd2gbp*@Ccn zz4=1-r!q6&b4k^dpGrYI6M&io$yUmHXpZvHrC--={Wdnl+RcqOh#&u`LEpS$-o$!y zOTw=&xWF>O7B`SMWeShQgVw;~^>w@_yYJ9m8RU!Ukh9VVT*Q}mrh5ClKFh{ZiZCuA z$PusYcthNVAgPZC%=^{qXWTVisuyqGVWSi9e>hPdJXGyU%?0!qm6<}{N_==^-l>*2 z%-+5*vmhm+(?1z_`OA}&??LOExbD%1j&9bTWY0^|s(6Ii3> zzHQ3f5SpGIXEu9A^Q;j5*!sqxF0EYz^^?s`*%-{^k?|0m<`aIs`RI$-Bkxn>`5=AP zipJwm#V;l^>ZI{+PX}G7r1Fvno_KvnK81?T3Hxu2 z0eYaqr4hn~PJT_(D-<5LhI(`aea0#=t6}#aT|c&hLAPZ|6Ud>F4hRM7pK)_Sf!N7XFAP#GeaeGcGeN%FE`()Nwem)=4ON?uzB+ERTBmJ&1dbC9^AdbNA|a(Vm1zR zs7r+#(B)NqpTe;5lN5`6g?kiS1xEhGblkkFfo<6c%`X434q`Czt^*TUvKn2Fo|nMf ziEKsC73zeu)FNxcsi_O$Q(Br6gMyOg#q&2;x*Y>$$%}O5;owtzfM_Cjx#;Buq~WGK6lhd6a`guF|E7npjiVWfAWRg$q&|G^lHn8o4+I0 z58F7uQYKVOSkH9zZd?>&%{}1^_-sXeZKIp7Yo)*i|3vtCYrg`X!|8^__5D5EpCwJN zY=d8av|HAirmTN=(Jfj+%#7pqz)YQcC%we_QjO9IR1sjb>fvI7kwFY^S?1)6_zpuQ zh_nb_8EsI7t~Goqy0y(9uV@)}-$&Ow`!1?rHNHbumN~m=lGFX+;b}mqCnDPHR>iyE zj|m2Yulru*je-$5{Z^bOeT0%BJNoHJ2%R2sz&~;gpluHx=3P3NdZ_*Z4ve2%9+6<@ zY=kPXAnh8xX%Ino9_#4wa&Ks=?ena(2=0Sz707=XDXuaiogJwsSl6IkgO4*Me3}yU z>)S=mcy?jFvf)bYu;)qSXL^dMiZ25S8H-=tC3LUq*H1H&FAFWQhJxl;x)2R&WZl_W z{{~_Hw@#}Eec{#}Ergrv0{;3^rmBp_|G6S?^$;RkEwC@-WZtMJr+f4^gNzzdluww& zb%lmJGwRxKW2Q)GYylntYa?hym`hTZ!|Zx}xqV5tU1>4=!u;erw+&90)46Bx4wltM z7^HxLlBRx6`V$|NC6{9A&dasX+XJ#WMK0`B8uwPgT%aA+?=+7O0o2>`P!0KB?{GJC zgK}&KH&$m8N0BE<6~>VE!hEU9)s;LFbTK~z&cqXnm*x}{ONlM6d;<3DDr6qLu#U|t zSf4~NpT(()jK*YxQl6g#Ng*7@JNYj}y7_dJzOMDG3N+UjwO~A(_wzu z1q~?xdt8dKBoYfh(}FjQ9N-u>_~QB{;H&4b+rY61bBo&}YoMqCDLV{ba7wrz+%xG8 z?#<9_D~mf}Ax@~9_ItM|nITac!#Difsw*+c9S@}ZKWwkC2O9~5x2n*Td}<#IQ7P}U zP65$B<_$wJ#YcJYlxK2Q4c=lxQ_3cxgU#^Ny&DzGDy7q7#*Sa zKF)KTmhGxR$xiTd;ubf`(8H+G?XRDc#ya!$^&KjC8aCAee zW1WOPoiJ#$oDdJx@aqX&ojel-edQ%&?ChwDDFik~$A|M$vjWNb>b$(d&A6um;KIe# zkn-QMGcm;t14OeC-u<@i?CN}xjY+B6tAs?)6_|3eM3iHQ8sRwW9E3&-OTy$X8%i(=`` z%}wEzS#DLkqlq17CFy4^N%1BN3(UG!?+_Q=qgurdJB>eot}FUyfR@KzS$P-RrMy(D z1&DWv9|>a!|+dU&<9b4J@z6bbzv7_>i?4|6-Q%o{Ist9vHxoZA{yR_rwe7hTDJ z!nQUp1d1$)*X~=VyAlDW)T{w*jVKNc&VRz~baj)`|5JecAF3rxK zyH@joD!jv~+9o5^USaH}B`0I6zyuo0z5`Z<{|td4YTPRKTon-=3?6P{!BYk1?;{3F z&-sbr>uc9H%)-EoKmK!kKqYuI;hN-~y{Bf(`Vb;A+W7!|sU-Qv#c^5nb*DWG3(J^` z5esO&&E^$GPuk_2swCeYeRI$%y*GY_)pK*M7lWl-p%+>4^SCH*kz@xLDNuJd`6tUg zfw1O0x3cJba2K^TR+5qnVp7;fG1tIueTIH#i(CXV&&o!;ZE+F*lF+Uq+#pVfo0Hoh z{|EA_i@RD`Rnl1vX*HW;`0E$L(E3(ufKc=VyzX>Y+=isoTg26tx0ue`b&srpW-}e_ z9(He7FvEZyqaiJs2M5q)2;?sRq6)%=UjIQ$Ir8qN0s}-CR2y=am4ezcoe9bA^1flQ z8Dg-cm@pr%!@m|=uf66Nl@IUh*U49kl<;r; z%*Ue-`bZ*<&Q!fXqy~9a|Su&=%e)v$Dv7ccI!ZSRtmaJ9IhjA^efu;{4j zX@pxJh6r%mPOoilhKKM6v_yQ~uxti{@-i-V4yv!M#RV*5KMV(z7Q2n5pO7BmO^@l9 zX<}li-aFs)U0h&pmn1ZLHKY5ZhO%DQe?6?Km)LAw(BZn``R6`r*9)<>7UA7&74z&s zMkFvP%t-#G?LtNf8-d9x6gfBB8}xp4FA1{binIukXMNnU^K-x^o20e*%H^BLhs3il7Jo|*g< z0!rE6i;|iE70?^-BY0+XNVfj{H=sQD`@nC9jTX!buw61EzkM|IKO-A}DKRfGC0cs> z=Z1gB`8>FHfDQi+=KriHv{v`>#KkuvWBq9L?jN>B6#Rvbl>oH(ZTf#>fd4`&Xj}O= z{s1fwhA(Gswd+s`?7+y6S@`4-uAE8eH0F_ zp*4HzWKhKC#=|lt%xn$Z^KG)Gw0 zCP92Zcahqfkz+$)k4pNy=xtTG`ivR>LH^#&^_Au5+b^rWXV0&lfVlPJ0_X=@`CJ-e z{`VkJN6I4>dEI^7->{cfR#(Xzs;~C2U>qpjbd@k>d=&AyKF>Hp_OMgn}I=_T;xQ~4Eq6=$4d>It)krH_-0y+zqG z`qsQQ2M)`wGsV2yx%o!`jup9U?1DJBLk3j0k$Ip43t$0Y@5Lzsn3LL}=cJ9%rZmZ` zZ!TKesf|2-K$vaI*HmYn2Jaz6ig%zS`6>J`mob6lLk@$b(BKm!ecWPEHz9Uos8B%$Inh?o;V* zB*6lqz5h0wOi-g9e(|z=vkNPvK(B#46&_%!=TB5%k1ga{#nh1TxiBB4Vdc6)z8fii z3Zld9Q&Gf0k`QZpt|Px+Q~{1`?c-;~^#Fd=$mY&HL=ZD$w`FhEzF$oK_}TsEwcG;?ehqeRlSB4SK}g_o`tW%4p7a|P zu^ur_*5*R$o(9d5SuMRCPdtZDp6wR^H(*cyqN8P~S$bj3;9Zn;-fMLiZ6;)5HQn1k z$fg9_kgPB%AFTiUU|i|0q{X?jt}_0Vk?}jc=V=jjYA zO&Edd%o0`Jn18nqm&3bf7vp2U^d$gMahG0mE9XbXjHU^Smd2AN1-{>F%U%Hpierqe z6M*2nO1pZvA13^BViPVm^h^Hwl7U9YpuejofNh~m4a5}D@Z>i`^8e*I(Sa12ZU>kB z+cp0U4$x5xn$bZ!(*K)l{%_BTuF3er&0j{ce?H`&&jx~n-|_eVdzJiiA5MALl;;?i(8vD}Zy{nZy(CQfj5EtH7t2*`ujR` zSq1^|_7YuAuEP%YH-kT{Kf2__fjS(g;pd=fblG7S8WEw-bP%P)a-e6(<{@<7&ob|) z=JrX6ge-nwEiczwWbs5qX^cp=B(()(adynBm7%vFkQqTI1>Scsv2feE_)DhnuMkFg zuw&BpT7@!ebbf^zN8*t0^mH=M>3c>UCh<*84*28BGuxa~v;Xt*XN)XfXJ`6Xe)UJ? zZ|K}E0nskyK2KbPUL(bUMON0=V7^EuuNH;u+Nl8|-*Eg>g*LcwIjy+aO+ z57%8R1zO27Gi)Q0Wd)ZOHdiQM{lmCEgB*@0n<#k`uCiBT-Ao3s)w+xG(T15sKBohr z#r%Ah8@^YghhIe$J-Z$Fi#TivSB9hdX<7Z$BJy5CHC<8 z42e8rW>FRB3Uy}zit9KnnQho=`Z$=tjh-qq2t{XZWiX)0gWh59S;Xd6p*sDb{1-gUX&&s4d|Fg$X&Ohc+` zVZrOFd4E#a3BiE51&jD`^gwYN+YP*4^Eo}1qY%G20^MhFJ;#&W{NVY!(Tpt!uLw|{ zpK?P)${Q140p#|Bn)%7o|9hYb@3hl?CfkMDsVuz&KH2z zX!ATyuC_Q126czh#+Y>%X<-t){V~a_-5BQ%R9I&7L>ap6>Pd?cHvc);D<6{Mi0|TY zE~J0+#3PRK=TK!`P}1GFbtq-lTX&Y$1_r$}WRkr^PzZ{)3y{_W3!K9c@sJ0qkc6di z@PMTvU`G8H{=MwPUY(<_Z~U?@!YNrz=8aWo7eR+$KQJl_{REdw>YfAd3W=ub=vHG( zdiDMa@Bs6>-;;~uv(rz;geEhd!nRwqx%O|C+l$I%#G4BJ&j9F7f`&8kl$Di3dNu-U z-YUFjy`F6gu$4Mvd1mH@lQvn_y0dfR!)k8R-bZ?sy1GJU<+Z5;PK%sFOgsPc+DKyZ zq@30hY+zxIw}Z|Uk|-Tw4i(9-WQw+oe*5+w3<;H&C`}O17;R_;$r0mc!4MW^U;y~8Ece} zUERsd#qj|dRnO$Q^C#8$3sxA+qQSeF|Gp@1-G*hFW2(ke#9mm!3i0{PC;x4d)%m|Z z<;)!I%6g@F2F4@b;wjgDDjm{TNyn#Wn177%z5rvz-Cl>hqU(#&FD-3qSOR5F_Q7f*RP!- zf5Le*QdBXgPUlpW4oRNr8ug_!bQ9vokb3wzc{|t{P`R#p;p3;MXzP3Jn2pB0E5(1K zOsEz_d^6&$bGJk?AeBBIKL3YTX-5zd4^OV@JZe7!wo2&4($mbETlsuAxkYy_FjR5x zRuI~G|Ly(3ZfE`w`h>or@Oe*nd*JCmT<`m5bS$AgdB8akBIaeZ)Y?K5CSAd{iq5>E ztsJ@C|5ZbOeQiZUMgL)-tfb2~5$+4HVg1)hcj@U^d~3d#&OO!LI(VPpgKaP``6pNa z*q)9h{L=?NjZQ=6k2n+ui#d~j#|mI;!Px%Z;-vhnMY-=25xZjU{rQ;8K`EIKc3IzB6{7_IG_d5jo zeeb_t`2X$RRTSu038)NyLq0U-{9C|J`>u`fk9I*$9n#0j;EhTPXCLR|W$d=xT>8ji zObO^2yQVXFFqr>(B3ylhK@!&mptGhbY&*=40wik%Y?~k` znY(dvR9OFYH=u?HEu)isNcP-cjhr4lCZ^sQwhGM(X*SewryQ~we^kP4W>x=0);QvG zk>h2L>B|`~6j1yNg3Id#xXO~%{3mc{&sgJ4%ccGOJ$iU$QC`v;zddEDi}y>K#EMjK z4uN~N%)}_%(%pynBvOoH-xOPKv8|`Yph^8Ms98B8ErL)s{PIaN%(wHv_u3< z-mLrEKsR%Z(N!u|p0)X4@k4GVa+a{Z+t4uU?Uq&2e8X%TPuzA1^6O@EbI<5)z28Vj znAGLe=;&YdmbKbD)(X~;ravZ=r#{dq;0h|hz`y_mXD8@~QG3p!#4R)TT3PXZj>~nM;FsulWmMhS=np~*7prP{uae|i9$5>`4zPgCY3Z}pW6Zg{JeyU7%{# zZMC_iM1-oGBj^TQu(A>fbeQ6u{?BRSe7bVDeFy;z>bGfJA}up*mEDmM8a^yJn2*lJz7{sHdW zLgr67Vr^kbWPo`C6Pqb_9{r`3`vXJcvQKO`mY1V1l$@*>#ezWR4eEDt(hvY6;Go!mz_vde)qq}1Ki>- z_DM$hidrrWtVrM=QU{6w`w5_WPo9m=K2Kdi_Q6W-5#J3mWBkZEU(q=zPVx5bUG8{= zINpo8>p+dsn;xy!Zr?wpqi-!E8S}=EOMr4jNZxf>yURu-BJ=MLGJ+qzyH7)l3sva* z{dM1f5CmUt{i0yaaO}-Dk!I#6U@rAeI(*JR*LL(!K+k_JuWTe&+Xt4^Xo%~c2C5o@ z0i*Qz-uaDYlH>1l?k=xxRusF&m$|iAl|0r@;xe$>^JwUYfBPn4OM<=)XoB&t@BO~~ z^Co%&_>Ti%C%J6C|9?LKJo@Fc{e1;`-?_Zi-v`i;^754bJ#c7l407!aT$3$zvVX2g z8<-xi11t)l`_P=uspIN}wXg?}Y=b_i5zj#KUMhK8UNHD|sqXS>TrJ)#e#B(WpBEQm z0F2wo(aQSyOWq?c>_wK?@x&k}L01&#zn?&DcmKdgV(_qid>69l;yk16&9sEsRW4N0 zza|lk@l1kHdRMOVxCut{@`vm3DeLG)RJ~AU!~A|`K!!DL;6=@{e&*5~@rZRmL0{p4 zMb&qDvsZh&D^=G7a;(DALlng=`A+~tgkNIhSnwh{^VJh$$f@&q8Br_vR$VYkaRN{r z@5)J!m{ot=$z6x;_2pG!1KhUs3(Iu{Smp3U z0M(v!H^K551_nsTGlHIl+3SF1b<8Q?5W1YD_Ai7E{iDKvtxr=VZ}sk{0m`P7bC(=1 zDK&ClObPKo{77Hol(jW%AQxmkfy`u9OxcU(HdDUc2Q&GZSr&jL$G-2pOzJ^vIk;A| z3pka2-xhqkHE$~|G-Zfmd3n_}MCLdqc^>Y*p@z7yt=3>c_AxysQ$>998z9YT9P^1^ z7xUi5zsGnv`6e8}hrcR!SuL%rr-zd?HF1;`=$JHmo$f!HcU=|Ui{IL0F58yx0MwWU zP&t$gmpejY-Npyp_fU8LJvGy#!dt|~Qec9=qz2WlSL|&?plC+{5FArcgj2Eo(91x58A zh=nOHO{ZTXK3|B3p8Tv}i5VU$(+}@r^_%B!%ivJ|{qA!R6j+~Kk?d%Wc)cY?@nyof z<9cry<>!Ib>-=_mq#pif&e7{Yf+MW|l5^sFPF6i?e{0x^+1!8$s&4=%SlZs?UXv{U zu5ZAT3J)G7_SPG{BWtrAD1Luy_-g4{Twt4X8shL6p!h4>hk)@%Pd3XI`=7=eoOGn*MGfdS%o znQ-CvGW^>Q{L6Fv?Faro@c+v}EOs+t_N7Z<(ApmTYn6MjTrv1)_aFsu<+6LwSccX2 zdD*iI&G{36oo>VD5c1>&GnZv=aM38nkn+bjZAu|7jMi>e#AGy8w+1{wt0iy+7R@1l zd==zEK;qO?b`fIkr8ePwu^a`I2q&%3;0uniUry(Ecvjj!=41QJdEKB6_V)ckP*kjy zt5L*^-Ix~-i=IAQ%w!0AYVd5wmB-bwrdz;&(zK6Y`pqPz4U%hon>bcCMo2slpVbdr5aW^TUC zg0E@wzde0;0lYbIQ0D8;aaxsP!z@v;=mNMMHs4M#7r!VcYbqJt(8vWJ7HEbfV9 zzTn%9A$1>07)W&@C=HbScg4&HWJu%th&9f8oHyad^}`SpGp(`V1dD^1gdSbqxGVUs zc;yZ%ritoS!3Tg||5Pqw+|UPvIW5B;LqudIepdF2dDG>+EVd(y9rDyu5PHozr6Af+94;#mtio~(3tLfZZs>Xy~?E9uC_^_+ol50PHMcDCKOvnn4llpusB8P0H&MIb} z4Uk+=p^l+7-bl6$kT`W#C_21Ne1Wb^F3J|Q`TT|3ZjTOBv8j^9bmrB6RXVD$3Af%2 zdtd*ESmSA!@ z)z6O5S3Yg;x2;pcOpaAWE&QZd7lK|JC-ENs>gAbz53%py2t7UGUzr`{9MFrBx{r+J zDm#32>U9k&WV!$|xjPs|vI7DdM0II-MYs;c=QUXX^!(61FMIsFaP^e%hF!P**1iA* ztk7jFCC2?((6wu7E3^i*L2%D&*JLp_WmV0M>(oDL3WTt%`tl(t*IY8-k!_*~!dAE# z_QL?7XyyC!rzJ(r$#8K~NPsv)mOw^I!88jY-usxG`$+6{?k`cUk@Ns?LsjtFIysDbAQXh4Ww%?sAa6?5|Yc{Xsg9E_O4W z+kNQyUj^^7X^>RQODF#CjQL!ke@&6irPNrL@g=>N!Z_B*3K{D1D2%-|e-b9A{?&37 zgol_(E`t;`;U}2Y$2T;``9EXW*_yL*{QOHq>DP-mIMlXelmxRt(Vh-=uwL~;`c`l# znFb3Bg%Pd`f7%#oP!1Aw*o@~uD5ly}%+e(R1Jzu`aB~eDEhfBSdU3d$eAkq$LxE0> zH1yncur4i-r&3AVnDE|-Vu=^n!}tw!1EYX)jlrN*eW6|gc1-Ddh=Q9k3=u)Yw5MFv zqIZOQK?6+7d=A*lm9@t9!P_OpJFK$=Cv+0zmSbM4F``Iu0*5>$O5@YoqPM zkT;mc4w93uCdEeUg$C}6Q zzAdIhI}R|LHc5I149EjLHTsKLIcaC69}WQg8867mHB=6@uZ50d>0h{gL+(9t=%C-O z;!&ms-!0tT2xHu?<(29}xM-+$C6PU1lw; z@Oh(!m}>8Xq=28IPMOOrf@|}P4QtiYo%{F@?BrhXBuxD!iL%ue~nhGRgo#Y z4>V0E)Q>Od8PGJbAWv}9|D1+g+t`Z;JI^UuSiNU0uC^(L@P#TGLk?bls%hEZ`xRAC zL}w=u{~Irm~_{|ZrjR-*yIJmifk@Rkn# z0S$3*Lyn#H!6<;@#qZloP*Y$*EzuJJP>_r)*GHZ*1JUD zj_GFs)*GJEo6r&dpsvog$=*&e)%Q-L-)K@Hhf1DBP3J1gilVIraK=0G9ob(sn31D3 z)gYk?V>%a|-AkaD5+hE`_3ahN<9txuvL~;3+8_7$E>{&BBp-3&N7ieJls5LRA%*~ zo>syAV>+~d0PgZ3cF!U8vSAMp6e||-ebm&{pB{UgqCbas-}ZXQBjkZL*eZm3?`dkL z%`@laswAxs6&CdAm6OtP_BE;+(RBgTwC#kepkYzqn`U%dR;Jl#b~-+%Y`{6#S*^q5 z#Fps;j2eR$SFR^7??bmC{c{ZMI6Wo!&LA9$Q`T~8tYr{W1uAMUy-w4XO}v=rNr>{wr__7^ zWzmO85F3@n4FfX4`qdbw`e(SD&>qPmmwv+$(&Oh9Skr@iU89BwE95QTZVqkB7(D6wjyrGJH~xoJ=Y2LYgeF znS_KXHmYd3Ur%*+S9F3r$5oDUs$yebzlt}=!TL*6Y^%}8f#;LVO=Y*L=b@3mlT0thUbj+#?(+mgt^=Zu5n9p^GwFk z{y5nL9iGqKhNXyv@Ut6LL*i7-*8r9D5g+GYv9 zQ_o)gg3Tp6cOY6BU0qYJyb(N#6Na`+HNqn*ofn=waoArcak%+w?tkJToLZ{g8Ijf` zCAff&G_UzHmpNob?yxoa& z-XS*6{T0FBT99hktT3oo-94sTucZWsl`!esZKoXZiE6b@(!AG>vOkjPfO9=}({w_F zIw2;A1J60??yj&3KKayJPv(S`lTS-h@qBosxlueyv_Ul0n2>Ux!lwOQ)7_io*W3Ns z-czk9wYDx0`B`-%DI-qR3D<}|FgGyZa>i&=JA^Q4n$l?r{kWD@%LffpYWs#2Q2#NB zabw|`-Xy!2_SZZZT|5s`sq(OTatu(4%XnPz2ETezeZTjQz3bC|Lo<`7#)FWY>`jg1 zD4Kf6wPq3UJ?W{i_@0aUU5BXGb-H>Qtfq~hkAZM9pbplSOG`cb96`k{ifEa*b&CX& zC~WVjk|Z_R*YZpu`5uqJq-eEduoG@?@b(?RFm5{~--aAu-_mf%fGplg#F=-u$04gO zTUj{L5+@I)fn0rK5cSCL267&EpQUX}i*(;3lfQ0S^QSPlE47D=hwPWB9=zC+WCz+NHKN`ojIK6qV0jGFt;9b~NfGyjDBS=ekcMA;C z-3=p1$Iu}0UHH>yKYKrWzwh41@%^XA3^Q}z_jRpnt#h5{xw3!GmNUT)^cf7-Uce1q zV_thac=jR%P1q8?xj+D} z$0G(DuY5o)oM1JE>7$1a_5w5p_%>$a9(=3dZpuO7>(fw9V`6F1^rr zvXSz|WQj_QF^sK{t(pX7$Kx3sS+mAK-5>o@|3N~nHsg$QERfTCSF$B%wb=K$KNGl} zXILd?@~Jj~wAr;O2q54%(ZFE0P0|)T4m+4Unq66pIXX^GL1E)PC1pKxiRY>y95?-o(PBTdbRvnN4npGt6o#y4R4C?kN=|UfJey%@%SX?!C0x(OEq0k^J z_`D}Ac3%vw^H-h($|>l5tjA-+572*IEsm~j;#xip3GMCc$B2B-OW$;4TdDezih}`$ zwh#6MO3_RKa;iLZ6^}<2CF=#>II7v0^T5bB;;XIV+016hA`#Kq2j6+ioF-hE3WJ+8 z1bSG{tU5g`Jw@e2rpJQXXV;RpnFqxV5s04Z;e*c6~ss-&pd+d*~zmU=SS$5oBgdl-*q10Oz)j|X?eMu4Kn>th> zk6BeYPa%+g7;J+~_6YH5WI%Rqh3Lu~bkKMr2_;lOgQIhVO>7yWnDA ztv7!B5+mftwP3ja15xBF#X9`tDRMeGB4OP z`377X{x0_@mojG9bXKMg64FVJrE~Be11ng&+^(W6i_EO+N5{67zXi_}`6f?c-U}EA zK0tox4f*}M#=~quR129zMFkxaqp9*v@#pIgeN|nbWYm74TgcC}g@o27 zceE!Yx@<=lODic&Hm4tzW`m3%9ZNWMO|fWt>Fc^gUNOZj7&Rt*F_>VQjauura6XuT zFSS}uNuM;RX}#pejElz#%-p=a(!lN`qXBKLrNT#n7$U_$Rb;i8P_PPfBITqu22B@O zO5P>yV-F8i!PDIERIvS%$m5>JcMB7Ua2tK+DI~wZKa<~ZqG?;Ys9Ubht*IX?HcoX|XPJg4RjG1(eFSy+ll!apVQek*V)Nu^Voli(^NqwKs0FL zPNIw}%LEePZ4)uqSxAegnob1+U;2}xDjzharou6=?VD$TkJ^eJ!=ei-Hg!w0trWUw zoLTmQy%JGY8iCZ*m>L^vg~3GhZOh^GTTu!TYx%wa+@=69tTk-L&6 zyVU&Po+M4dP&dx2KJ99aYgDgdHnw(aJ05{6JFR#02`|LMKBJbQ*RW{d9!UjU#}9vU z%l#HmG`mWsJcemEEV;P%lSC&pm<*GU;#Yg|)b^hTXPC~M{^OYd`GX86{Kvn+VxUvF z?N|Zk;O&1wjQkBHga6)Q%>VDLtp62|{_l`#DNcfnsOwU@K@$d(3!>A{)~!F%_`I&} z1AHs2hd#j*|CVesDy9H1;&HNIQCM*q4qJ9vU9}KL_8P7^LrP7q~4!Ar+4h5Pn@CxIPA~ ziukAN1V`>wTd6hW4kUj10-^7v329KHN)KZbk0E20yWmE!zA+VD{%8J zJ&$i0-!~@Ff>OJm(YMaq5_W*J)nv*8u#H2mBcsK*H03V7v*TP&0O`QE!xJ<%bgocY zA(?rvOTfxs)Yh-sJ95j5u$Z)=wSRI3Fl~aAn+2Gb(jw(ZLBh<>*KQ$>M}jh$hGb=V zKgd-!6^sbkhe6+E#AqpwE?F5p8#(0#Y&h*sEgsYFuixeA8l zm41I1=;bNQmv#X(y9~X#Y3kY5IRDr6I0di$b60^!&+&k$^j3zq3R$_FUHgDM}&hIDh&*5j_tcLogFRSnaMY-R{VCNVpW z_4T-plxlJOur_^wn9}K+{9)z5PtXSNmuO>_zfS$M9f|zWKLNBW+Tp8_U({!Y2z2Eh zv9Q2HqwKFw19;c*=ZJ_-_HRpm-0k(|hfcqS2IzpgVD;VjwcbxZM@ojiXZ)d-YW@+Q zK7vkOrl=M*v?my;IENXrRQ;t1VG-q0T!JrRpZ&g{d%7WnjD2{vS`?~viAVr>bQ%ra6&3c8~nS- z65u>b()7a8##B1?w9DG8r`nJ0N4N8O&L+-KB+~NGL&fO{C}FwVbiT{9R%6{GjML38 zRUi|2{PC^_8oqk|oe4q_YjiZczLBT05E>yQ&if5!q+RyhoCfm*zqbKr*3@o z@I-5;`eX~kLKeN4sf5`ks&b8)+HtQhz#rHu+2buqA<0f($P#Nm?{MSM&RR>4tk&g9&K5TsL*7 zdj_nA6#KucOt(hW4ZE5)pB>O_QuwJ_L&SVYyqPAvhUw$8p|YOAx&_ob4ef#S_!$QV z?3f1^mq1>-#t%drx^O=obno z^|Cbgdt(PiUYT!wWl@t`i76R_pnu%dL+wEz)0^jaK8jgu-P6Z`49cAwrB;3eM^RVp zPZb;D1n;E5l+nfVZVH9jY1LLer7iiZM{StLxn8;b}Rfa-ElXv zcp)oJpSs8muDuXeSsEPC9%Joig@}3ae^r?SHLYgTPsiK z8Y1bh&_knwkkxQ$c&}{29-n-pV8PwMfg52w1e%lCbT-_XlWssUkpSYA2n2hAP5 z14n^iU&qbeA3$c5?n%(d`E{i;;Y*CD`WwR=qh{R;YWh`bsr37wl5Qb48{Q>P61<1& zNj^YOqVdu!76`S+5SS5vfp#2z;c=Vq^*WcB`_*okK|qRL)B@hQkQkVR9o%MO`YJ)u z4ExzIX`09T!3qMN&jmYiug7*;Ri0&tzQ}u3exeell)AgX$C`h@#B*a*+Wv}O+6~>U zDvOZ1&fZFr+q<&1S=?!U1#_H(be`iCKC&f&{R9yPtB~h59DAHEs7b@|?!`>nc#NG~VW7c&}-WRfN2E8A?U{=o9qXVWvhna$w z^an`BJs?%6_>%wqV?|LBx5?C%xBWWw= zw`pI1Z8QqHF3?y+&{~f zvh}gv;Tk=v*BgfqDRnFGA?wN~7Q6z(A=lj!iD27YwO?||F0D!{U2*3$wZd28HHJW*P|t0@#nM%f1ymkEQH`v z+5L*f3IC*qIe|_mxRTY7@P)gPS9lVp=^ks!BC6Rb`Pm5^PNx1txH_~2@&hl%5sD%l zh4EMy*P*hM{9);GR%L=&B&>Ik8JHp2%1-ky=P+27T1)ld<8wEx^>{G*W7AidV<^1* zFIo}~hv_-y1+Nil*!2A%eFbwe-3-p}<~y)3f{t~TO_L&TatbW@f6!>%3SvYbB*-M$ zXR;&d>VXN9{?ZaT@c}3JwsU~!M3MoBGOCpM`FY6aSq}49cJ(pLuC`u2rm=nxwB|?Ma(Cs!4F2A$PH++O>)B{At;^e&2{@xE|Tzh68 z9DH!-hLGNRp5zdpLmVo9p}C*KO3!U*%jKHM{ls}RMaU5WEB9?)#=t@o!yfT-CQ!z% zCISB2BkYN;LLNY~J>RVQZr7X*ldmf+>{URVd2r;kr4jgOYUy1Z_fYjxEr7ryAK`vQ z4fhqOe}l59?K-48cT*>);d}pZp0IOeGT_xSAo$BTd$LZ>_vyqjTZ?j-D-LND!MT{M zc~bakQUq$5Z@Da?bvabdRs3RW_O)F0CAzY|j=Q*Eh2y#IoM7~XlCXS)SzlC(z)IJ& zV;-A_aaw%d9GUDqZ|A*Gj?J6C z(?h)eZgN6;&$elfGBr;iJ{K;-v9ScdT+RoAV8nqO_p8Y8AW}(Ix>efFHR}Ub}Q-ZBU+E zcAKW+wdM9ZKF?{{Ii0vigMKDhZQCt*E~;NU9`g7r5>7~}mtX~qg`o67Fc^YmRnTz` z*0697dXDO%KDO{URfkLlefaQXUxf|I4`WFyB_OmBTS;Rvi8C8xJ)(I(9R3P8>bfl)@6BOWgwYVr`%V&e8_Z1gw=u)gYFSu2CGte zcGlI&{{A|U>ZN_A$+d#?GF+ufqf`(Ce8&UUK{iKWZwBuoZ#z}^jd z1v{Aa=Zwomv$H#PVMWK8{Ptq&hIRI|Pk;rE+YEZ>x_*izun1qUy&_V^K0s*ZTw+6N z8>JqiFJi^+_b%706(2s0F3$S_y(MThkFE{(Wa#VramZ-&kcLr<oz6gaA4T(U-(myVz5osWM=KHP;`bX|O+D1m#ZF ziB+fv#PvckcLlS+7pD9kmi`SQJWu4k*U2+&_2!Or9Fd!!PDZGgj1N)_dOvys^RW2T zHcn_sE@SnwrDzHIV#Ui6R4rm{a`a62MaaN){DHI0;Nlut7JhA@L=7fZrh3Ov z3}7v-{?HtPNy?ss(&B(pTMo3a~s5*efObafzPJ_1r;GB^#ZvbKPDyqeGdmok6nVxi4%~9!3z<^yZ zjFc7fmKJ=wlN`tfZzX;4Q-6A>X} zIgMoxd%Zo%R)L2Pu-0(UPxZ+Na?JaDG~stI=uzxA1p_q!P$06dcz)qB>tg!^8ky~^@YGVsW&Zf@8+9XCBM-=dgWa{@@N!j z*o7XEz$jIEr*X9JZ5;b2*SHVgJ=@4nYgh&hP2ZgiTYx#2VQGE>DD`u2iEFfk7lC}B z9h-X{$ak$5d4Q8S#-?@1)7`KV{+!8HpmERt6LDiy#gL``dfxZA%IL8XKwdZ@YVU`MzzmRJ9vez(8l@1Oh!hy3=6fB(S0 z)b8Kt;D7nc|Ng~)gBTJ$0@ByH$6%5rC&^06FxPsZnL_c5YL#?*IWYfSQ`Ws|^UMHt z4Tz~v?heC)9O8;nm<>{Z?GEq~-=uwp>6S1X`Wvp4qDBW|_pA2ryr|01Tg2r86hogL z+#6w7ZDe&ivAg+J-jgp##K$0;s3@nE?jEVh%L|NctSwpwibQ`uDRMBL{^T?~Ui9!= zP`~#u_?Rb=75}H2tnL`xHuzvX;6Iv10xgX_PFPJ*4d^C*?{S*zT2irDj3WC0_BY6d zjiO~Ut1C)?55(wnn9i-19&j6gOCFt-X>9CkfGYwv>aC|j)6_vza;LT9=?f=Nv#=x7^VeKPV+Je zszb3dJ)z?-!%=Pe$3c5GviW~gbA$z0#Fy2@D5$7+(20Y4=tF)G5G#v(5) z1Ah-TYA{i;866viEKcb1RquUJl_Hh&|EqF)*Dl5nP{-AW-$nS)hbAr)0-EHz!Se>5 zZfK?;FDXcz4c{d>K|4MzWzZ-K}jFO4OPFM-g(uk8R(z&Wk=J1!}zq#{6s zUVS5lI0u;TcIR28=5SmBAp)%TdO!YDa7nL8ubZ+|&Vq?PYEN;EQCo4YnSz#&IrBRr z;(D8T0Srth_O}vn)KDP5OR*pLIl+D($>N+#AR*Rr&huDBp{p$Bp?vYVH}|ZYQr;tk&ovK(p@4nKPUL($Luu@@53@&n2L+% zC@q`(#vI4g`vbGSXV#RUVxSfQ@DB(dh(lXmc=_WYq03U|zmLM6n74y~dIoJ8)H9y9 zt}d1Homj#7=H`OebDIT-8vy-F`CHX|cguJKi0~tYz@UQ8{z*i$`SxjK$l3GL2w)Ha zHTlOsL><9EaX3y+6sk(x;^B07m0X;z54dKK0&z+hnXyRf5DnuB7{j%v^W)3w*^Vvf zxBaH)xABkv4p2o%Qk5=;J09sKyl%)U@_Nv+v9Y6Q(6_Zl13R$46d;SZZJ*U5e?%aK z4Y?<2xe1Ab^1qFnVNxgh_aPuO_czfe8B+NmNTv&R$_R+bb(P_SylKY~s(nQkO^2m3 zxAu1Y$)u|vdic}KNJXN=j2Ye#-{GhBass|5A!mSyFS>>)N!q-PL509*Qz+aUI>016 z$9*2fYN2U62cS-6$eC$@I1%v}=y>bZ&B{Ns1CP-94T$%bdO@6wH5H=z)_UlCf_m_} z{OSe$xE~%!jwI_?<5dT(SQ~Nn`_mMhQfVtjiG2~eY99YmHM$y{d<442Tf;C*m>bm^ zA_kIwVhTylAVeM1cMT{+@x={C;NJ0%=~!;9@aUhu@rS5_c6KV4`>zY0dmq_UiW7m7 z68I${7g-_!=0n|?Jgxmwt?E0WBfvH8VgW)YJqBI><<~(QIG_tEZh;w>bU=kpOUD9) zR%zAOfULYf`OVV;a3~2IqO6)S_`DIfnvDi^B8vc_VK6O2e;%+eKN6DYo=!mx4gV)n zC_d+$!hQiy(_d+6*uabHOG#jr;01 zIn&Jm(oAM3CQyW~%F5=W!9kf1;@6klE4s^feaY7M`nw!)Fp~)No#!pE^_pjML5dRNXkt z%npZ4Y&9sMtxXeSpQ1>5erfTRzQFcz2%Dh2`b@}SozDPUV=yK)0?pQQaijlW4fd#- z^bayLlj6X^x~@WI|H{mEtUBEPenW$Mj_$$n+myOql?H?ddZDaf{L~~y&hC+joP&}F zXd{2Y!gCqmUcZ^<#;Bz(zg#ehiZ>*W@0L1y~6@abpoTj_UTo^0ZbU# zczs~{OGbqxJ#5JF6BFah2%;nf3^<53o)+ok^c+ocCP(*sKE#D*rP#2NvUX5#iAtOU zMXj5u#9OOqF`P82nrBXc1j3{j=L@-ie)c|k*o=c}ezPZeG51JS5qj*Gz1@sO8*cLf za+8WI`;&>sm3zBW@-*1#>77jGS&xhBA=iqOp_sZHEn;R?|8)^5T}U{Cr!}Vzetfgs z#htMzF1q`v5jrlSMBL59cvnGbX75~I{)zx+zmFLhE;Xi(OM>l*-F_KC8dL>bPirqJ zt2&X>x~6DjJM_34++GOR_s`9qj%Bvvi*yV2(gA$_?;6B7ByDd6jnCg&s9?beDD zE>atpNO9~MrOYEQ?9o*zF%|o0an!70_#cM6eTV}6Vm{{Hyiw?Ke6tf9jZCO5k#!*S z=7>1nGLpaWT$laiNq!)kUe-u^0?D*6+MKkti$ngVLjf^w2jp{d%4xJ@zPSdsfJ5+& z`vy_qQsm^Ww+cy5Wdpvl+N2|i)Dz#8=R^kbvbI~i(W}`-~;!>?} zh=mn7e^@8}hh=$(xUkEw;lqeDS7~-uVty0`p~lio zsc?zlebiqes+mt6XjL~88tywkX29H~zhd{x2DFdmxrXpm0poN1@*a110C1hsYJLM{ zu|HgOqn)sc*K1ZPQk!+aA(vu7)0+^07M&8r5G%`Tb3ZGA+3zg>ICmURw`;ujQN;Q% z@RO;cl0ejB>*41$yx8ceEj<@3Kl~4Hqn4MK^N?tV3F;sXzb*=}=yBk+F|15`C5>6q zbVXu9H{lwC9LangOZqvQ{N+=}Eh@y7wns@OKH63n0+b~`kNod$pf0WN|7@w83)Keg zd@N}zweyVwFEPJ2d-YDb&o9MBaY%39Tou$5elB`_1V8fs%)cYYKX2%&%eo|OVxSy4oL2%C|5ZR zzJx_T{b>=#p)$MumQaWS3B6M?TNK*SZ&TC@B2LCcbR#Jv&lmf#hcqW2m`Y8~=^pbB9Q6Vjp+(Nctt8=p;J!Zoi?Bc7GmE20n0P0f++Xt%7rz`QK%tpbzJOkc~G zZ!P&zxR4S#Ac#@Y2NyZ{%$cHMeNC%-lVGI|TyZh}R1Wqq;tn0r*5 zzrN|Z&>KfRZABx8L_yn2m!CGrGS8nNA4qitchUS)jbXxZp25Qgii23zb}D(F#3l3C zd;sI0Mv8CQ9|3N?l4z0ZQ4p~6)5&4U+ViF7Z0sJXd0Yvo787IRg7kD8e33H`Rq)g5 z+UlxEg*-RN@!j-w^dd16o}tI(;w8Z7kfh}^ikb{gU|1krRGp+iP~|b~KrkB@RE&}6 zAHuvMCHnk@=KyzIx`gQ@MT57ySKrq3EW<18cHuTXoV~F+d@}ikRhtVqEZvr|m+``v zX`9nMs3!Qo9tH+4MNqg;4(B=h;@R_be-omI$rO_fl23G5kA9T?75+&`_a%8R#&U+c zF(V+9l!B1^l04faSlUI`M_K5X5%P`o=MYY8s#>i)C2MXT<`PNg>*zqX<-!pHohGlV zfZhqIBqKlhKwRNmtmYYG)YqaGds&9Y1{hc5gA7CyM@KCbS!a*@P5BD*bfLcx9%X-_ zyB;(BpiwKY>(i23cMX_~pjeiJEvYYU_5qj^1L-)^lhpo^My|} z0n^CTw9EbUE?jD}N|TT8bT_rZeqR47_VcWBxA0t_`TgfG*ZzL)7h)@m1||%jseUrX z*UTZls&zka^I9Fnfl<1itH)VT65MMVJ;}Tj^&fMWy<};1?a)~K_O4P}sxLhuz^K1r z^eM_rI5^r2s$_J*=$*F;BIMBL$Zsdpm~%I;qfPdoXBn@4(vbb9);>!lf%ysw>h|eo zvYXXpRyTyt`fx46e5XZ_3fTonOFVvoK0}^T*kxtnxdNOu=g60De}IKvwWIHSG;+U` ziux>_KJ|1Y*AGOqUx%Y}XNG;`oaNBl_G0MC`>U~mf0nBVSHjg=v zEBz(qc&kz|LnXvC>bpf^?pz5Qo2iOeUx#Hl?;CFmRN)lQ`;CgXAY@K^w91!}wa~LV zq|BgR5ODADTn&fgsT1)z=o}(=iD22i-xXMs)qWRbzyFMOp>!vzAqUO<>iD;~J`EB* z8T=Bqjra#u_zP#;4siI5Gyt6AfBfq=M)LP9|3WGL0}A;IU)-XP449u|9G2UR2WKYM7&5(9roWBM zKkJkVruFArFzx97y!rO>fJ5K!Zy+7;C%x`BYOPCD`+iO#V=TOrc@?(EHhWUB`pen% z0Wu2mq2d?N8dT_(c>ylq_s7|MhIo)0Ub+9UE0p4QcFPJGbck~xeW4MP@pvK2xG?3-vacL+w%Oafs9D-nSf*@QTnGC__oIKDZs+m^Q6OMjHFNhbl?SSd zyLFrwSYu=l_R~?0_=w$n`(`LuQGJ4PLIet38DI#BPVDd9u@r@*!_T+~=cjeCaYkZv zu#FRHEG`;d%G^q2m^sfus_}6;Qn2%?a8!~rZsq14^q#bw)umlH?uZNlXQ^0Ys`6Rt ztW2xEk7*f(hvah&p`X2xz*C6tBKchNMz4Q4`ZPUD;2LU)pLF))zRV&=4SImrrS;fg z>|XnZ@w9fw#bOEHOCutJ=ilq1f(vK~cyGR$MpqiDPUzXZwHj5@#2H2;(43wl2kQBe zs&&@$&yvV`M!grEbl91#8JSaAwz_@AX9K44#D>slWObv&U7uLZ- zdR9~>@l)-7^^C_1`5&V))J{tWN$U7yHi7KkQV$VL^bD6Ja*txJ>JQog2 z86o%7poi^mui`Xy)F(iQYYC}a0z*~4wPYMfIw^~XLw?X6{8}A3@N&S<<>3$M#PYhj zlH2sqi&O~iOm4t185r%Go|`eQav)~5pa(7;B^IWJCeJ{u_`Ug@h{o94m=E*uGh_0q z=>?#Kf*9B4-9&E2U0XyYkv_FIAPEMENJ3UWmm9JMs^s^Dgb7ima(>O`Jt?`Q7zFAY zu-pOz0{tOK6*%vnzK@njqyUqXc9HlTQgUNch{PAK-Tk0upR^o(-IjL}si1|)njI4&vcs!IC65GEV>w!NCyv}$hA zzsfYra*Qo=FtMA}=(Oman`cO?_@q&IyV9Ki1Rt}yhN2voPs*=7_;*#etf*jyN-01Q zn3k3{_z6d#HB?1yc@L^CGM}Lx4e|;guW-PdM71dXFUuE_#ed2dAJay+=vi&JZKkUl z1`Rjw9z~qec%`%7Pt2R+5nhy5II8kt>{qFV26=xcX`Hq(;)$4$TJTmk34Bf4?b}ds z$QCYi>7prH-;piJWw&`V<%~EDpeFFR@VV=K>OB{syld$Pl97-K!Symp)z9}vh~J$> zYG{P=cm=-=<{b9_(!3v><09_n|D!5$1IYo=QRh*R86I&T`$0W7^UdH@*ShRe>HFdekuHtPi$n`M48`VJvUh%Je4ImW zcq{+BcUwl?%iDh+b7b%qHYDBszF^UwH`7wmP&?4W`+Tn57&A?^Rg7`G5@}P^z3jEaZ_RtLG+=)gKz8q)dWW5~b>%2W} z7C!FWRG<4R&gdIYiV|6fTtL$(9 zY0^4@(_sNr@PqR9zzoxa&-yU!L}yHkf$bGf&}@AmWjwt#&afQ&R* zk3)*mEkaZ_HpU^=5NlPIw)Xr0%j$Wf%C&pc3`}?u49*A~$ zaxDtXRJOQ2{7G$f@yK7y9?0o{Md9sP8vgSOZiyOxbPVYT(2a5Ia$mx=Mh8RtYltd~ zY|ODY5dC5M@rR^W+eWHWrPo84IH#Hy!i{xRR=<{i;IeW2b61wzEu}1PwJ{qdOCFn}N#Q*7R|MjBb6fc2|(#yJf}$ z98CBt{2B(Wh9{d^atihKv)1iK76D2WJq*+Vf-X*5jbzvH@HX*BNYrwjHCIGeTCYf|$R*6#z`}w6o-Ej0h!Vi!+5!C|oYY|v?8eWgQ3RvsPhKyh^-< zT;SOp&~dZaAAl&Oz+8cvWN*5)t|Wem#ZrJ=w6i0TH8MX*+_Cu#o=^Qe?O-*K#!>NOKyn->QwfmM8iMeIpW z3oh&1KW8V;C-^I&Zh}bQdGHQlpb>%y*?GG7$n+$0m@R0i$=gV|*#l02!Gq|#dnug>hQk39p;t$M39;RshtN)SPJBd@_XHTsmr z**m;7LVvE>twQ*6P}V1P7smza6vR2<+moQzK2m%(!#-7Qyo_eYdD`k1<>M5j zjgb)Ez8A~73!%CQmF)Z3^^qj&fLJL@FQ~{&vS{zjX3}p_I|!-3)NN?w%4+<|1{9iV zZcGKwoqk<+zsvMfseBN}(c-{+x4fAIb{DEm{dZ-d`$P+Ao$r(Q;UgjD?cqh}LysY6 z1dEQi)%(Xjb(o*3oQ>#jH^$lP`9TzEZ;oqA2+_=$Woj>G;K*HUKyG9 zzRw;m0HRtPcpguGx?(A_PE?AOn=P)gg`DM$QMmMw6=;ONjFm{t%m0E=NGmT~-G$m` zwmFkGVgcct$T}KNb4x&ONm3*`Ns%ddOlg(=?_Uz=;MvQL7SP1s{9sGho%a>2r4g4d zsCIuZBKW5bF={@zU{Q~PptG_SW;2%ba^V=-*q93x*J^t7MYtL?Qd*)0vEOEkcx)US z)0?a%mXwc=Nvqm8>O(gR;WGlP!+|lb$tJJG2|hT=V}bi#z5=dpzmu~TJko_J7F#W& z!Il>6K>d~*PFll!mwYYg@@l)pYQoT^b;l5@jDI5p5;7E{%gK+G!2AnvwSXSDA``>r zreheCl`}<8;g85qm+p==H9rkiB-G7F0O5``gfd*0#B}f4Dpp zC`rYgc5=V%6+`{`u1d4Dpk;natg4Pj{q@P$s4|gVEfmFPii3t_7MeiwKor6xm2<9$p3)(=E!g#zEa*Z zz}Hb)USEJJdUXI-Oi5x}O_a44!5+JX7Zv-keT*Az-@SpC3teXIgSipKUzIclo)Ctu#jsA5FTt>Ba(O1O85Rg=z!#G!6 z;I?m9^5)@9DFRnl9+0rU;a?g9RGZ&73=>`>2W|0TKI5Qga+<&D-38Xc-MD63>wXpk zI*kH(N^NAz5HKWKsrNntn|8(Bh8GjgVK~cMny52vUD3?U%9{iYmc1z3O`97SxI{s( znEB)tp&vkY1Trd1E1Ni+>#wyBI<04Xge-x-ZAJxBmY1G>m_txh8AJ`IH*^+sd2%gG zV{ksFM28r-5c6l{G=Zx4DrbNcHR(Fn7vp)ucVh)pwX&!@-^TnPioo@ zZhiXb4)Fl{K#$LhRjQKwYjB(cEVi0}XLDuK+5w8vuM7GLzkxa7ODha`FBjW=!FCSf zv8&;-7r~r(WO6#L%N_VB`VNuD*p^^~LDwO;O+i9x8>I?VuYkp{lM{cvIM4S$Hti;n zfz{nDhCzch$0tGPc0mcG^h3`R?vaVIgKX;ns@*BF86O$Vkq-eK9b(So6>;gCi2cYY zZ5v9X&?X#q``4hI1vM6S+WE!ulw=*C911BT!9C$?3C~N^XtD~cr+qWzzQ#Pm#$edp z`h9MJxJ8Zb(~BK^wsp7ovUq!F^pv(Onb9X$E$L6i1 zriZ3zj%U1nwQK#kLgd=YLmYz7QSn8wCSc`=l@u3ZkaNb;b~&WxwB^F}54o0)Yeyb@oWk+VAH| zgh-|cLM=A&-+fo%KIP?`+yw0q$zkR}3Rq}A-4yq%y^(@Q_Jpt@Mg=bzMQz?sMzn?5 z$}(V0WS(PtdJMcI{waK^U_TOWn?Dtw9Ooyr>#SBNhuEG*<{xleUVbtB z$@Am$)(T$A27*`NDS@X;!dq!=FLRaZFLFj>DqA@4g=W5O{~=UClWGTPs@;mRD7|e0omiy%f_f_D$SuI+qkB z>@_C|%jLr84Uvjgljm{BnPu;1TbH6WEZ+x#G{rFbYc+2k(=l~LM#cz!p8U@s!xkkq zKe|3cH~6i7wef@8oJmo3T*Su`r=p}mTFe12Qqf{nwOc`A;;xCn*c3q=4fYR;#EvR@FMj;dapd|;xKVr zkgaTC!gqIfH&UKBZkcEd8LV>zpB0diaS*@GwFry6$o~0c^l5XF4O6cB6Oo_jx=lpq zYjicQmmQzXG+ph(0g#jR#jNlhLlSO-)|`#o#o{O#?9S>NLpuma)Xf_JKN+5YuK+k( zyX)@v{R=j(m1T+3s&!oM>jp!7+f4?@20`xxWc+s5`S=Nqp1m@kfSArH=3ovPSrmg& zpme6}blYeWG+{V)xOuwY@g>myjyIr@#TtGVmXRcS3;qjVvVnwtZgMhCUH(_QV(Zuz z6a6~P5$*+YQ>Vo#R@o`kZ2fCucxv%eefQ|0FPvr5vy*%Fqe!o>mn{v}vq5K_J4X_b zhZL?8U3LXZz}?K*iesyqaR8KsT+)TfMH`RZHqglV+LrKPiOyh}k4Ys=FFB-)V@R%5h-mCQ<0=u;|d=pxY02RU7P}O4J)*M6Q-a}yI0LokL>7l0@ z;bjke)T4j@_T#kMtX$Z{#6-hvd@Rw9pN~*raDq7UBS`$yI)`i|no#@D?|rS_p8;i3 z8O?9#sP=VxdppqLGxla@XV=%)KgSwhF%zn}Ed_2b+$J!(>;qjU5($^F+0|G|+JzO?xI8I5qi zU;Ovo`{U^R-~ZjedSrjE>;D39`(s7@_vin;6aUc>`#-+$|8&6-99UTj)5F8^Iyxzd ziHS)`1o-%g5fKqlQBlds+A1mm+FjptNPn+^q=>lhpvZG_a`N=_G&D4Hbad3x(z-N& zzRo0Q(AU?erlyXF=N0*LhZrOj6%`c}6aYucQqz7H0(qSItr3ossBa@6aF01NGn18r zqhj&TYFIg^Vqgd#enC%A2H|7Lfwj5<9%9lB#%*xuE#V@m=%qnU^dlO`CjzHM@L zai~Hah%mpskmb}KnPbztnCsZ8X;whTF`2BdbaD*S{s=nI-c9%K1fNNY?&#Fk?jQ^8 zi#t0z%gdu5tgNhLW@V9}HwbMb(rUSKEqVPeishik$@BI<*a6mOE(uz0xJYkT<;7H8 zPA8{A0h0HeuTPy@=b5y$wD~gSlP6CiBO@cWM@B}jue~FsrJ_e>XEjw+V!wRBMMo!b z)Ou$3K7A_n_Fe;`X8UyKfF!z|k_X5OoQT96!^{H$JGTAoUrd{snSo~z^$|6%UOUaz zv5Ae9^@T4cE&Ye|<^?w;n8ob%H+D3i^Bz6~qn)M({Sfo|i_```!6#Wh`YyVYI+#S7 zhhvh5H(EbTer30?5#~Cdn^m8(`A*&|>6#s`NqKm@$c;Xi;hHvh18UR8H#wu!-$x8C z7>osU*w*M5frJ_mi>*lcy1yBMT-hz4^ouLOEhvfu2R2n+6a${tmLM$t0DXFp^t++o z<~V`@6>-{oq9TdvR$n-K*J%`yeuc!nI@K<7%F&Ptv5|u~76K5Lr7y0Tzuh)?8V1S~ z5GBpq1A;ealB+3N7@2&)$*gWv<@ma!;e!ZGp>6%fPswDh`9Q<`qT@GJLzpWqunnt5 z{T*-^@G-Fp%Zm~wt?UTYSaP{yHnH0MY^;-1)oolBPmIgW&kr7dK}%bhl_flP4@aM_ z|2e%1aDUL$)a+2AHt$qXRc&!Cs&{wC^!3GrPfEEOA-OZu75~5r;zX>GruGOC=BX$< zoTO(uD=28fgs**v@;lVa&LCkQNRjR)DIXK7{x~aRB+4ze+HWCt+xWi2f@8CSq+8q0hl&{w0p=PPqsp`O|Fw1=Kv8Vn zzUP=w@+eV~41(kV1VN&NA&BG*14xu0IcFuv5F{u;L9%2Ra!vw6mLNGJBOpOQk`jlv z!E^5S?!Di8w_eqwtfFkXXLj%IUTf{Oe*e{z>q&`AP%-cAu)~7Fwr@9g@WN>f?C*fA zG%smwS?>Pb~0?rX>#Kt3jNT-VNM}wSx6OFIblmz7h zhP6s;04kylvj^+AUPaglVP7hDAC6&v6rJ&?36zu{+{a$t-b>4bwA#m4>CL*jx=5UJ zx}p>xKla+5KWeF5q`xUf1gD`D;NCkqQ(K|;O81v-;D=%ZW)Fs%oD3hK$=Z4-qME#i z;fT5v+>ki&GGfPWzb_Kl`=NqV;-B2Vsq-rI)-IAxQnLhb+E2QZ2^2 zp!n-QR^GM@CdqRy;8*{>0XtOb8D@q6#CaC-?lS43Hc*LRlsy%ppr zR_I~U3+!540#c8eL{`rbVJ+2LXM(qm@}4=_Igd-lwb_Po9Mq~W(ofHx72G%8nwNW$ zPks1XN!J^O_nXx^`Y{q^7nj8TK-@(+fX8P`np^1^Ef#>OKeAD)VpbPR_W9ZroF&b- zU+!CxH9JINe{H8xA66#zjcAT~XDQ~sFmn7xkjl6BshCSfTtiga@owo}0!60kS*lP_ zF=6i(t# zzH2vdM;2$-Z{&`GJciF|Tv*0pas3#D-fi49YuJmc*vF&q#UD~v-DSb0D4BsoF~^~9 z^vR0*_deh*uEkIG6fD)S%?&iA_NovW4b--Ff6jw)Kk{IzFJVP`qzK%+;$IlJKSx2@ed6TK7d@5r` za9VzyPmX$&BhU0`?8|H0Q)O&w6|9N!^&9sxgU5#*$N>6O!Zy~QT5OgZV{91f%*O^> z5<3sljnL}%ckPa@Es@0=)~>F5wBP3ta`f;J6mDKAtKSN?F^q9!9r|smY;W{bCt`S1 z|9i2=!EDs?EgzS>5q`eZT93@ajH%i0bicd~uJKn!_~?tWa7oowm@SdxL1fK0gFDiq z3+ViUnWl22SU}gzC5nil4+f1(-)|p=ZT)ESg%KO%z-q^MkR{k$JYb-J01SKn z5(J&~k>NX_Gd$Z9_4Hy^9NOdPg-Gm*K=*zln1+5GgzL9`jMxs|1CXk%^g6M*wXH1j z#+Ymn=A)^9ZRup=@VY!&^_#rF+cesP6e2d8SDX@dS|Q8wowtvlVWxczbi@dLxT#*| zy^cR~QxoUAV5%yW`s}jNP|f}9l_oZWW~9JAQ5y507>lHa=6c+RME)rLs~wkX4+jby zHxFF}#|x)dRie>TZr#`YE$J_nD&*Q0udEbojNQyAD#({ar@K5GW>*nS*TTP4_Eda2 z6JOO|UvgY_>n&6~pI$WO2#D0-{zV}j;_q_*M&oNc32IdjEAL_~h2Bj^gN@l++7W2; z-_DMOzSdWJB=)7n@nzAv4xz0-OYU3^e1dFJ8+&~=%{nv)ld>WH5e1XDK@Ft?N#CbG zf+FGJch8@wud%wprs=)acx&4hFHckDs1CINmk zlYFFetRnphOC_bq)8`Q`*M(3zh&Gl(;;5^k&NsR{+OG+BE?tR(S+7G@*`;>G9F*Ve zTo07(uDJv=*ZxMb%KTeL`oz8;?z0pZrf})>1vaQh!BjyMidq+y>xe_Pxt|7R-cHo( zDX?ak_mhYrS?X9G8w#_|Jq*l{9T)$6u)OieQo!x-N>JV04!u@mUoes}t3HQ<)xlk$ zxZZ0MSwB;ge4JL3RVm8Azy6`O54B0xr`J-lfZ3om=;O4NSSV$YK5BCNePgeQI?P3* z?bxpCu$L~w70{7N<91hNJ2Dr0)`TM1B{OezCLo*9l#ZS>4_+Xkr4<=Fx&vGSrt$$- zwMBiSbqutN70~pg2`hD-;?qu94OIrBqKP@`_0yt)e0!UGfwm!0s}EHa(LB)yBbI-u zS?F>AW|98(t$U&{7CS#11ELR3Uqh6Xlp?ht_Li2>(un&qh{w;ZtRT%zdDp^x#}dD~ z+OMy~#)H+e=;@Yx(!u8;FH*kiMg`+esVBR3Vea{NF$an0I#Y|d3JRm|2VU&dm2D*GnOmK z%9MwqWxc1KIwSm&?+#N|lhpcaH@SvcQ^Fwv#VM^+ApxqGQ($aLlo_4n4!aK6Z{PEM?lh}lRx@X^L+HKy&VU?9a zzMmTzoMcU2&tA3ijA$wZ?Ile!PBv$`Q=M`}rhOugt^PL8m80I_^bC{e=Oa{o8p3$R znTW9%Tt+i1*efn^F>2<3m<}lB%!R>vWgAz|PeEXf(O{uY8ZqD<9l;I>u_YEsskpu$wD8CJZz^k;vd8l1b!vKpg#)*??1O+|Hq18b`yqki zUDvGujOy}ry4$y%931VfsY0nxc{Yz0)PhO`6D$p!y!y@Ejk;sMZd=x%ylcMcqQ4h} zdN^o&wz;|8i%NtVYU<}Gpm)P%q-m~2?2N_-yixPC9bNW- zCQ%*1i9XS_ff}kC^jas0>O~2?5!}h*B93@9EXQ2In0N=3MJuJdr_== zD!D9jwmj@0Bx=hA54-kc;@3)R>0VMcvbWxmV-}_5*|13&FQU=T+`?MQI)D8BSr6<( z=6=IqW|*KMGk+jjc+LA>Ktk?w9YH9UUZtDy&pW|i20&>-L31yLIka-a$(-KP+ihO~ zifV>qT$?%TOPEeHOAU9oEM{5lo_inRjrhx+{2s?(mCOfp6_roruA&uHWhM42_K z&2m#5g3Z3siVB`+IEZk`nMHSacLmz0VLM3UNMkfW##=Gw>dub8HA!u(o=r2Fm*2Ag1}h4 zc@x|7hRZ8Y91LyrJ7>Si?RRt;Uq&rz^>QXWa5rq2v1w%ASG*KaWTE@ z=m@_WI`!r@Rd~Ga#svGoSEpf{y}{U1Ss6k_PYH;)XA2w{ow-ErzNfGI3`Ge+@=rlv zvg_6^UO1nWx(wNN4$33WXP$`WaPjryCmlj{k*P2}NV7q)yqz72Pd%Lr(%B^}E=~{S z@^e=wIyXcW?rzNHW{-RCUVaO5YFUa_(m5SdB|aWWB5;;q?({eY&w2los~iHW_w&z8 zit)OS%X5A|{`7+(t+|f!^=ENGvP;SvD(z$-H%dg6xSagTBpJxNFX5t^WKb^RUvP_$T?4^zUJ=OkCa54Rql_sh4}Wt>v|%kU&R{r!kNk&ZDPp~4^JRm9tk0CxvC{9;i$zf2J%ysaUwFFtJwh)i#%j_K+rkS`ZoWl{ zQb4~ohoV#nK;0!hZ)t&&Zp`I(Z9;rpv2Fnf_0m;uaF;TSR`1sK(qT4_?}vMc!cVLq zA>)1~mTZSyTG>gkQPOQfZ(@0#%ivXPENJWRP2UBNC*U*PqqIr<%JWJcI5?pthMv%- zYagk90VApau+qJO1ltf<3v*DYqpU|NoOwdh_(*b{YhdaIy;g=x-Itu#T0NOQ!QkC#jp{Zz;b1l$1Wuj%J~)(o13__oYL55UDY zLU)2o|8`iSs>4YZF^+$#wu!U}t3gsv(i>e$KiLdDu9JL+rge-U$Rb^&0Yke!y1tpY z5m|GkBg4UWF+pentdw}h;>Oi&eqQUi<{^>wdlq+79u#SF4l|H9S}Vg2iVaL0XnWu^ zS)uQ|UsB2;wY5aP--!~OqEt;|#>jb&31dtTYwnObGGL&5Sw*K`&H}&#R}tpnX1K7g z@1ClOR;>{s$j&OdoPDc6y!XfJFz_gIc^$~+YlNu#)yW}T62)w z2g!GUgsm=6ytryBoec6@fO@jfq83ZBS@R&2YPqvMiz^))j@4gvf`m}`aZh(GBWXWV zYt~^_McaH>k0kOP)MS=J%3nL;X>y^A~vSy1bK9*}GqNd3kU8gM@&n zh{#DxAg*tIb(RavM}@z%`XF7=#T!I5Iy&zL?RXr+_P$zJTd%D{93hNc#GiG%#I?qSF~Q^Bn>NH3z@PpuarJe{+dDHh;_Nkb}JSM#bFY*bux=9(Qgq#&KO$NH^3G9Rft~!oQq7AuP|VU z&53O?;5#Aw#~Q>oucVnC%^a3wD?xDS+&5e80J%i9(UnyrV zF0mXbw=qDAu9hMN4PE8#n^BO(pQS0wJ{zY>D0tPY1wg(l;k>%$w%=XyD$1-iAf2n= zr3i}J#hovQ$+;vJ0&k12WXhsd=|QP0T;Tbw=JCKVW9~F^rYP$lVs_%LFPVgPdk@LG ze7a}LGvD4<7X94UM}|if5~;;tcH52?0*L{=3g+gx5D1-`gX!7XB^8B+Mmt3DQQn`Cg``f{{Ri-K)a2VN+k={D{ zEG|NRrFWa%ZckM)@Q#ahne5KygvmVS6|yHD1e*uE9od3tjyw3-$Q=;%aG7bIjxZo@ zZYL41S+VUgqE8i&JE;ZBoi(uruNVGuvJ2ygUT&c7MNHM5->08W z3?JwFa|ym-ujUVB5PYpQAMFY{7U9V@Tj_8 zlJ!`u)tnXOG#^;Cdd-Vd70ROh-I+GUQ61_2Dju2EM1;V5G|vMcu3CR;KOlQzpCdQk zpx0nXUvtl@KQVyfOF;=8mBVX!=ZF>0^igRB`pZn~qzoONr&#fYv#Qx8uRh zz|ODE&>IC{n>AJDGbvlckAZLf2()XQUb%QLBn~~yzX&z-2(Hzz`CTqP4*M!{S4OMS zMC4V=3)0~zL-A1x0Y%aIB76L41X$K;Nj&E%(XN z*z8Dh-0^`g!xuKpkFetKogF7+jf=Ma3~7k6EF5mk1cMb9ZG53Ca$iJj%RPDpxA?|Azqr+A`?nPYSN82xnkr5^mktkXY;9v8tR9Am=$ z{W+CB-#mpSWW>k{;jM)Zw}=^g?4Iy0wp6}K%37R`e7 zn_Oq@*Tmel#?F`RrVvL1%~3YY<2Z-2KvU&B$$8rR!guii^s!>#9H$ zRGpsJer$)Vf8Ik8CJ1UIj4#Z>sR-IR9Zp_HM)r4iulk$}=UX=yvl{(nYinCuTMJb~ z=m_F@vWhz@gA#Q5n&SenK2i3}XZw@fho+t1r~tEJ`Z2BMBtK-nwpRc)TSH4qH$FW5 zyHY#!oP=D{(AHL9U;=$(Mn+I5*|QSBd&Voj&v@XQuCuzbA}253Qkp?hQ{zMA1cW!R z*7Ts55XQ5~hhgFvImo?5&KFQ6q|ndRk00+E4|R8US5`9K66|7BTf7yb;9sgOQWM%( zR8%CWGNUD{x?^;5L2QD8X2#p)o_lg-P-wpRJmo)+K2rEi++gK{)gxMFX-(5T=Dz^n4MR$|BGtyUp3s5Bv~M# z6ITDuS6$Wk%DHH^avqC`8z1JXuzcM&2jG7om)(2y;N?Y)+oxRmB(3rC2vskjnsPW1 zWxvs@6kk?4mg7MM%w>OXrAI|a?Ui$-t4otEdH5||yO~#Gt5#FFmQ~4rO2E7*xDQyn zFKLcuk;N@I-f(c7SSS#lbq+uHIyl%%=Gb>>zr^Yc0=NR0j@aLWhGJupuvs%k+mz}t>m zhzgdC%!kZfG;d>v21htHo5IaO)&6K8fYjaSZ1jOQ0NmUgmO+@3^8qG|a6FxvI00Vy znE{HBJCxIj!08zr%T*|mtReplg6aA}dnzjZ$)<-j01t0|Pv8}J771L5RP#+&e zKDU~zC9a$$8*?RRz<||C&iBO(yGCE%#EbF}D2$QE#o@}fL+@k53<|2Y3{A@ggqv}~ zd}gO9>LOZ$S(?S$etu#NvGYLXKQeeY`ocCfdKO|-=6Kv%nFLeIDlA|UqZE|loa$!w0%%RzC;Qr-cC#&g z^R8}s*`f!h@!D9RNHdbz{{TnUlus)f@Q1WZBD4BH;|m~!4~Z*0!7VZNKnATLscQ9J3-Y_h`FA7LtKJ9Gk7mMqdMbNzQ_8PH zC;|!;&>J+c$4Nj<0u&P>PKM1meY|kF8`6^=OG2w8q7MP{yYcBO;DwgFRhI#Nm$(!K z|4&ol8F3I+B&Szf#0}b{vgX-sCegMmhE$p_nw(akb{%My+%sk#{&`Y z8l?mxJDR^06Im5ycvN~*Q}DW!a*=A}v&F>)($$BxnPD|4AHY`yWdm|1Oc9cJla5*( zkbae?hl!6a=4}pyzS%YpCTL3A!orV>0BHuAPDlxXS@GSRZFZdAO7Cb(eAO#MH&!@I z;s{Vcwem7hnsxjEv=i|6v!aavmE6N8vIXysU)>mWj{i>XSTbcX5CQLP{dN;*DS#{j zhHzJY(mTl3Q|Gh>*$x4nLMU|YC9nZ+hBT*By>!$;r;e)AhyRQRq+ck=9~vJacw|G| zA4LNg6KE-qXC`PsgU~n3J3#JxyQoA`$Y;;btimM|G{*w9cxeO%*^yKIwM=)G@EMRLA1fn~~wG_~I!A zzDJUxjYVU>zFS_pu(zHH(kJaV2%?VAgBoEP7n{yIX664R#AD@PwXw5(t*Dd?vIe^T zu5T`EA_))(JoVr10Tu#`ImxtkXge{<#38zv>Ehu3U}64$kS*KKOo8Q+?H3B$2MR18 zBuiO!P(JepO${2J(w<1QKGWmyhZAzJ7JEEf`v7`>8QT7NT&dYGfZYXDoY?R}H zjWjrx_6P~Et!M;~V6NLx*MT&^aK(6N9d#3&XyIKlz)*;YO zz@ZB$WyJXPo;DuilrgC0Y0TAZ2y{9z@lqF+w61=1vYV?Fre3}O-S^?V$?nU{C_MQA zKBMXzv|@lW%Np(K|MPD8ju@n}fYBN~jTXYNbPN5y_z9<$?K~|dU9cih>p3T&=(O1A z#b_B(Zn6zUjhAI7&wCx@1_^8d4b(MpLB1e^((XL)v@|UF6G!6XhqzmlIf^npy^B*E zY^FUrX0X5avSEy$?=gR>NtK}JJqkJkhWPn&{p`S4i#(e@=rseJl1<8QKQlfA4HHFN z+?Jr%rF*n=bma9~cGIt`I+=3DMg)NLDYzZY$ivU>-MUu`Jl6suHy>!kKtV>Xd2#b0 z04hME60=C8!pUck^cBMhYXCh6R90k&x2I+KKgDT#!=5rn`TXg@OBBj?272M8c$=hW zK$8}o5Ll}@%-bVW(GvO~n0XE2xM*b72%pjbz2f+Qa;k(VXWnU`hFzg?F9wlT_z9mHmJZjclT-v7K5ShyX^7tD!T_Av!~kI4kZ)>3 zXbWX~*aNsr7#(EZYfy`>5K4X)?eHVfq-uiwj$BV3J0D#62AGDb?_8g`jqnu6SBDyH zM-Vf)pr=bqdOGa8{uQ{i>FHipLHM}+=+r$27bL$EXf5!F6!b8OaETIj7r$?q`~D0- zDnQR_9onA1o6ni?o;KM4tp77lK-iPU3+QM}G$?lY3Qkkz$XHRH{M@)uMe!=ZMVMLP zJ=a2GF(rU~E_uJ3_NWTnn@IVzJbO$^dWPyQ{K|b8aF_YqNkrZ+9R*k+~ zkGd%*p8n+SHF>BsJ~_%c1XA8yb3ZF|n$6$5l-I&uZj!pN|16ly@S5@2PoBL+AEmfz zy(RVQ>+1QH>#UDAfpFntA0Q@e&hOK&ALpXB^4ju&U8TuHYHN&xN;yu}tJPn7zpUdU zK6DuwYH~gR`pXhl>67=8>env3)Jx6vEq{W$%>5rI=n~T6n_S+$PW}e}-@e%c@puX( z*FfbE@Mn@z(^JBr$%?Q`L`^TSHaeqiE#h!rfT80a94j@a5?Q~Xj6)nsw zxPsgo3#4K~;_J_OLs_NDbG#(4s9gAuN=bMtg{0w;OCJ6>X zEgFx7I&VMK^DLC!QTqveXNC6S$&iGMvtm>xMBRR+-sX(Xh6NQ((Uczlk>-6Z26vjA zGK-`#Pzov;Jgx1pC@xP5L9ODu#;J@CUWa3yn%aM~v-cai&DL!FoPdkm;k(2Nbc%IH zSLmLX_1tz%s-9uv7O^f%e}5MPJS{-F91KVIf^AB)ZwGOd&0T7i)f@*rP&Vuei5;N0 zqa&LPDt)Vw<;qV65Ov+xXs;>?7?d;I6#{LYj1_5+FL8MX%5*k=xT$|u_;c}r^QR9% z{QAkOXWZ_05ow&aK?!=N1LmjPSFNKwIQ(kz3)IsHyo=L? zBk+NK6hzPs=u6;BkB^T0e3)aQ*thr7SMcUYFXVISNSlQN&+i`sS`&1@BIaE0$BE!L zS4YlEUU*;d9#UMRO#D`mK@raTY9gWiH^Mrn0r`zj1Xddeu3-?u0Pz9I`gYzbaE3nI z)0Tim2kO}0#01Da!1B-do`Uf5y%}g>k~sb81*S{46L_NN$Pb{Gp7aCI{m$8D`d3@$ zc{?J@$nGOi+=BHr_z{4{fqqBior#`o=TKf70u*m;3w^`(**hZz$dAL1M3!C_{ zz7?X63rwLX;OmBdIB)rZMTZtPFxW<*rHTpi$2)1~X#kPQ!+KAk@b6*guzqd_iY7Jm zR7I>9(Cb3>#zSH}Aif3S9^0DducPIV2S9N8e8AvS9JZ$v)^T9Z|D z_8Wc#$|#awg;gUD`0yYUw>i6d{Bho=RL`s}UQIw(5LhXR%A@UZulXCBaEa&rQBC7P z1j?uG6n_S0sJR=8rA~+;2dPn@?6@$e5Pt^1O#)+3LqR?{H+hI9ka|}kmkRoBfgo#4 z>JCPYFFdWhKo~sMXWjR~y3kI{!G#0r*L@m;Oe&tfOR)`tAei>Ko|BFir2(Jl+5d|@ajhCz6$qW<=Dpr3! zH+q{1L-o)T^wZ%ttlf;=HD?7u1#T#?Lf~zuUkAaGy|Z7~hHTrW+NY$t4%L*6qC3oa z$4RFJ&&d@ntOYJiNy1n{p7q2KTawKMA-?|lT#Jx}^myW$RN;B2=_5eT3PFL~!9d5) zQD6CN(818T+FT_v*0xL*FoG0AZ`Y=Bjz2E^5O)vLz*js3y3xO~6j_R7d4fKxH>jWe z@(F5a1(?o$>Qz>owjC+9#ClSx<8KOoN7vsJ&~cBK&if@kN#0GXkxl_nS0m^L2SQaJ zk#QH0dH{Cbip&u#{6VRSVGvvkAnw0BO1_#nOp!rTG)w{FvvaE1z0*hq^sqVmK@&GR zLZ$Tu>b8MoWwYH%UQ0d#(!cxmIe>)(NZp;&XG9B)fl@hYy=SYEBj2ml@btYw#S0_` zpGlm;U^3_RlQQ40DWF7Hg97W#;`Pl`4@Ss^^7KNSB-wRvb6X&oA{0We#@6n8lvZY8 z+zpuJW(A(TEbaVQXKxqd+@3G)?c`*Y`#$TAbzru%AR&guM18~bi5cHHB+>4j%r@J6aHaX=Xn<}PLPA2I z1~fGJfoCrSL&L(uk>S@}ODpPKdRQLkLO6$Hf+ce~b2(!@LSy#cn7rn<9k!9zQ%s_t zQc&}5T=e1kEpo&&9yA!6c}H<&cD>!bhGAg$=b=+uQpmjlvTS1VZ0&)c&$Y3U3{B$}6u5Xj$ZR<1}vb!3+3nktnj^N}Pb=xOP z!TLP)*7lPfSlL{vpQ=7BUv58iLY(Fe*b#5{58r~hoK@@(V+9|GyEgjubJEsk@p&H~ zOm+$%)2(*#*MyPa;*em9$T0S_!s@+cw!Q8(^BYv6+70hVh9=vsx6m^RMrRF2N3FZ% zOJ;}r!S+4^I|Dt6o0SZHUw;E7U8Bf1G8+W#n7wU1DpGx|%8%+G2V#5i}7tK{AhrgTv4S8RAoXbC&=&;Qz&H(jI@>4;Kv5I&y*dzWzSg3-rWYFr5loXj$jEv7zXL^yZQ0uu4& zsu`8>q=DYht6;y&;7o9FaKQP2Il%eX25^cO8%V$ncd^I6-2l!9Tu2=7xqpFCfkXc7 zhVu_U-{bt-F827h8~)SR|F`k{v*G_fp7YQ6pU3mx_V~Bs`HvAV7(JQVE_JozZo|RB zVRC`%xR@EcmUPXGV_ literal 27837 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%0A%-BiCnMnNl*PI*^;Mv2h0EsMsHS?^GWw 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 ayq{0>=Wpnec<2UhfSDUx8&M5CNdEvT$72Kl literal 41551 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 C)gZtC diff --git a/docs/sources/docker-hub/hub-images/orgs.png b/docs/sources/docker-hub/hub-images/orgs.png index 604ed95a091e298442534d2169de57d2560dae5a..6987cd3b4e5822810143ca0a5c93ec7968914f1d 100644 GIT binary patch literal 45997 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 D*9eNS literal 36206 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 X>MUrv7W8iwXx`q_)z4*}Q$iB}b*#S; diff --git a/docs/sources/docker-hub/hub-images/repos.png b/docs/sources/docker-hub/hub-images/repos.png index f25bb3a48d4298cd052aba5ea3daecd3aef91435..4e83d34053f3282f3a603aa522cbd76ab1f7219a 100644 GIT binary patch literal 63242 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