From 24a5f7b94b37c4f6d97aca958f9533e0e78b1e69 Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Fri, 28 Aug 2015 10:34:57 +0200 Subject: [PATCH] Revert "Revert "Bump cadvisor godep"" --- Godeps/Godeps.json | 76 ++++--- .../cadvisor/container/docker/handler.go | 17 +- .../container/libcontainer/helpers.go | 119 +++++++++- .../google/cadvisor/container/raw/handler.go | 2 +- .../src/github.com/vishvananda/netns/LICENSE | 192 ++++++++++++++++ .../github.com/vishvananda/netns/README.md | 49 +++++ .../src/github.com/vishvananda/netns/netns.go | 67 ++++++ .../vishvananda/netns/netns_linux.go | 205 ++++++++++++++++++ .../vishvananda/netns/netns_linux_386.go | 7 + .../vishvananda/netns/netns_linux_amd64.go | 7 + .../vishvananda/netns/netns_linux_arm.go | 7 + .../vishvananda/netns/netns_linux_arm64.go | 7 + .../vishvananda/netns/netns_linux_ppc64le.go | 7 + .../vishvananda/netns/netns_linux_s390x.go | 7 + .../vishvananda/netns/netns_test.go | 66 ++++++ .../vishvananda/netns/netns_unspecified.go | 35 +++ 16 files changed, 829 insertions(+), 41 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/README.md create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go create mode 100644 Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 7fecb9eff71..668db1268b9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -265,93 +265,93 @@ }, { "ImportPath": "github.com/google/cadvisor/api", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/cache/memory", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/collector", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/container", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/events", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/fs", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/healthz", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/http", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/info/v1", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/info/v2", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/manager", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/metrics", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/pages", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/storage", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/summary", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/utils", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/validate", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/cadvisor/version", - "Comment": "0.16.0-81-g27fb6d5", - "Rev": "27fb6d593c6bffe274718119659815771e79e198" + "Comment": "0.16.0.1", + "Rev": "f99e491a989bd3b1b9565c55c556138d7e3c2ac5" }, { "ImportPath": "github.com/google/gofuzz", @@ -549,6 +549,10 @@ "ImportPath": "github.com/vaughan0/go-ini", "Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1" }, + { + "ImportPath": "github.com/vishvananda/netns", + "Rev": "604eaf189ee867d8c147fafc28def2394e878d25" + }, { "ImportPath": "github.com/xyproto/simpleredis", "Comment": "v1.0-13-g5292687", diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go index 2ad1f306c1f..2ea3d4d6cf7 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/docker/handler.go @@ -71,6 +71,9 @@ type dockerContainerHandler struct { // Metadata labels associated with the container. labels map[string]string + + // The container PID used to switch namespaces as required + pid int } func newDockerContainerHandler( @@ -114,6 +117,7 @@ func newDockerContainerHandler( return nil, fmt.Errorf("failed to inspect container %q: %v", id, err) } handler.creationTime = ctnr.Created + handler.pid = ctnr.State.Pid // Add the name and bare ID as aliases of the container. handler.aliases = append(handler.aliases, strings.TrimPrefix(ctnr.Name, "/")) @@ -176,12 +180,21 @@ func libcontainerConfigToContainerSpec(config *libcontainerConfigs.Config, mi *i } } - spec.HasNetwork = networkCount > 0 + spec.HasNetwork = networkCount > 0 || hasNetNs(config.Namespaces) spec.HasDiskIo = true return spec } +func hasNetNs(namespaces libcontainerConfigs.Namespaces) bool { + for _, ns := range namespaces { + if ns.Type == libcontainerConfigs.NEWNET { + return true + } + } + return false +} + func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) { mi, err := self.machineInfoFactory.GetMachineInfo() if err != nil { @@ -264,7 +277,7 @@ func (self *dockerContainerHandler) GetStats() (*info.ContainerStats, error) { } } } - stats, err := containerLibcontainer.GetStats(self.cgroupManager, networkInterfaces) + stats, err := containerLibcontainer.GetStats(self.cgroupManager, networkInterfaces, self.pid) if err != nil { return stats, err } diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go index 0cd4c119b9d..e9c8c615720 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/libcontainer/helpers.go @@ -15,14 +15,22 @@ package libcontainer import ( + "bufio" "fmt" + "net" + "os/exec" "path" + "regexp" + "runtime" + "strings" "time" "github.com/docker/libcontainer" "github.com/docker/libcontainer/cgroups" + "github.com/golang/glog" info "github.com/google/cadvisor/info/v1" "github.com/google/cadvisor/utils/sysinfo" + "github.com/vishvananda/netns" ) type CgroupSubsystems struct { @@ -74,7 +82,7 @@ var supportedSubsystems map[string]struct{} = map[string]struct{}{ } // Get cgroup and networking stats of the specified container -func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info.ContainerStats, error) { +func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string, pid int) (*info.ContainerStats, error) { cgroupStats, err := cgroupManager.GetStats() if err != nil { return nil, err @@ -93,14 +101,121 @@ func GetStats(cgroupManager cgroups.Manager, networkInterfaces []string) (*info. } stats.Network.Interfaces[i] = interfaceStats } + + // If we know the pid & we haven't discovered any network interfaces yet + // try the network namespace. + if pid > 0 && len(stats.Network.Interfaces) == 0 { + nsStats, err := networkStatsFromNs(pid) + if err != nil { + glog.V(2).Infof("Unable to get network stats from pid %d: %v", pid, err) + } else { + stats.Network.Interfaces = append(stats.Network.Interfaces, nsStats...) + } + } + // For backwards compatibility. - if len(networkInterfaces) > 0 { + if len(stats.Network.Interfaces) > 0 { stats.Network.InterfaceStats = stats.Network.Interfaces[0] } return stats, nil } +func networkStatsFromNs(pid int) ([]info.InterfaceStats, error) { + // Lock the OS Thread so we only change the ns for this thread exclusively + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + stats := []info.InterfaceStats{} + + // Save the current network namespace + origns, _ := netns.Get() + defer origns.Close() + + // Switch to the pid netns + pidns, err := netns.GetFromPid(pid) + defer pidns.Close() + if err != nil { + return stats, nil + } + netns.Set(pidns) + + // Defer setting back to original ns + defer netns.Set(origns) + + ifaceStats, err := scanInterfaceStats() + if err != nil { + return stats, fmt.Errorf("couldn't read network stats: %v", err) + } + + ifaces, err := net.Interfaces() + if err != nil { + return stats, fmt.Errorf("cannot find interfaces: %v", err) + } + + for _, iface := range ifaces { + if iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagLoopback == 0 { + if s, ok := ifaceStats[iface.Name]; ok { + stats = append(stats, s) + } + } + } + + return stats, nil +} + +// Borrowed from libnetwork Stats - https://github.com/docker/libnetwork/blob/master/sandbox/interface_linux.go +// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore +// we cannot gather the statistics from /sys/class/net//statistics/ files. Per-netns stats +// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relyes on that). +const ( + netStatsFile = "/proc/net/dev" +) + +func scanInterfaceStats() (map[string]info.InterfaceStats, error) { + re := regexp.MustCompile("[ ]*(.+):([ ]+[0-9]+){16}") + + var ( + bkt uint64 + ) + + stats := map[string]info.InterfaceStats{} + + // For some reason ioutil.ReadFile(netStatsFile) reads the file in + // the default netns when this code is invoked from docker. + // Executing "cat " works as expected. + data, err := exec.Command("cat", netStatsFile).Output() + if err != nil { + return stats, fmt.Errorf("failure opening %s: %v", netStatsFile, err) + } + + reader := strings.NewReader(string(data)) + scanner := bufio.NewScanner(reader) + + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + line := scanner.Text() + if re.MatchString(line) { + line = strings.Replace(line, ":", "", -1) + + i := info.InterfaceStats{} + + _, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + &i.Name, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt, + &bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt) + + if err != nil { + return stats, fmt.Errorf("failure opening %s: %v", netStatsFile, err) + } + + stats[i.Name] = i + } + } + + return stats, nil +} + func GetProcesses(cgroupManager cgroups.Manager) ([]int, error) { pids, err := cgroupManager.GetPids() if err != nil { diff --git a/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go b/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go index 51405db74f7..643922084bb 100644 --- a/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go +++ b/Godeps/_workspace/src/github.com/google/cadvisor/container/raw/handler.go @@ -334,7 +334,7 @@ func (self *rawContainerHandler) GetStats() (*info.ContainerStats, error) { for i := range nd { networkInterfaces[i] = nd[i].Name } - stats, err := libcontainer.GetStats(self.cgroupManager, networkInterfaces) + stats, err := libcontainer.GetStats(self.cgroupManager, networkInterfaces, -1) if err != nil { return stats, err } diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE b/Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE new file mode 100644 index 00000000000..9f64db8582c --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/LICENSE @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2014 Vishvananda Ishaya. + Copyright 2014 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/README.md b/Godeps/_workspace/src/github.com/vishvananda/netns/README.md new file mode 100644 index 00000000000..24a4003ae6c --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/README.md @@ -0,0 +1,49 @@ +# netns - network namespaces in go # + +The netns package provides an ultra-simple interface for handling +network namespaces in go. Changing namespaces requires elevated +privileges, so in most cases this code needs to be run as root. + +## Local Build and Test ## + +You can use go get command: + + go get github.com/vishvananda/netns + +Testing (requires root): + + sudo -E go test github.com/vishvananda/netns + +## Example ## + +```go +package main + +import ( + "net" + "runtime" + "github.com/vishvananada/netns" +) + +func main() { + // Lock the OS Thread so we don't accidentally switch namespaces + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + // Save the current network namespace + origns, _ := netns.Get() + defer origns.Close() + + // Create a new network namespace + newns, _ := netns.New() + defer newns.Close() + + // Do something with tne network namespace + ifaces, _ := net.Interfaces() + fmt.Printf("Interfaces: %v\n", ifaces) + + // Switch back to the original namespace + netns.Set(origns) +} + +``` diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns.go new file mode 100644 index 00000000000..2ca0feedd5c --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns.go @@ -0,0 +1,67 @@ +// Package netns allows ultra-simple network namespace handling. NsHandles +// can be retrieved and set. Note that the current namespace is thread +// local so actions that set and reset namespaces should use LockOSThread +// to make sure the namespace doesn't change due to a goroutine switch. +// It is best to close NsHandles when you are done with them. This can be +// accomplished via a `defer ns.Close()` on the handle. Changing namespaces +// requires elevated privileges, so in most cases this code needs to be run +// as root. +package netns + +import ( + "fmt" + "syscall" +) + +// NsHandle is a handle to a network namespace. It can be cast directly +// to an int and used as a file descriptor. +type NsHandle int + +// Equal determines if two network handles refer to the same network +// namespace. This is done by comparing the device and inode that the +// file descripors point to. +func (ns NsHandle) Equal(other NsHandle) bool { + if ns == other { + return true + } + var s1, s2 syscall.Stat_t + if err := syscall.Fstat(int(ns), &s1); err != nil { + return false + } + if err := syscall.Fstat(int(other), &s2); err != nil { + return false + } + return (s1.Dev == s2.Dev) && (s1.Ino == s2.Ino) +} + +// String shows the file descriptor number and its dev and inode. +func (ns NsHandle) String() string { + var s syscall.Stat_t + if ns == -1 { + return "NS(None)" + } + if err := syscall.Fstat(int(ns), &s); err != nil { + return fmt.Sprintf("NS(%d: unknown)", ns) + } + return fmt.Sprintf("NS(%d: %d, %d)", ns, s.Dev, s.Ino) +} + +// IsOpen returns true if Close() has not been called. +func (ns NsHandle) IsOpen() bool { + return ns != -1 +} + +// Close closes the NsHandle and resets its file descriptor to -1. +// It is not safe to use an NsHandle after Close() is called. +func (ns *NsHandle) Close() error { + if err := syscall.Close(int(*ns)); err != nil { + return err + } + (*ns) = -1 + return nil +} + +// Get an empty (closed) NsHandle +func None() NsHandle { + return NsHandle(-1) +} diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go new file mode 100644 index 00000000000..abdc3082904 --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux.go @@ -0,0 +1,205 @@ +// +build linux + +package netns + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" +) + +const ( + // These constants belong in the syscall library but have not been + // added yet. + CLONE_NEWUTS = 0x04000000 /* New utsname group? */ + CLONE_NEWIPC = 0x08000000 /* New ipcs */ + CLONE_NEWUSER = 0x10000000 /* New user namespace */ + CLONE_NEWPID = 0x20000000 /* New pid namespace */ + CLONE_NEWNET = 0x40000000 /* New network namespace */ + CLONE_IO = 0x80000000 /* Get io context */ +) + +// Setns sets namespace using syscall. Note that this should be a method +// in syscall but it has not been added. +func Setns(ns NsHandle, nstype int) (err error) { + _, _, e1 := syscall.Syscall(SYS_SETNS, uintptr(ns), uintptr(nstype), 0) + if e1 != 0 { + err = e1 + } + return +} + +// Set sets the current network namespace to the namespace represented +// by NsHandle. +func Set(ns NsHandle) (err error) { + return Setns(ns, CLONE_NEWNET) +} + +// New creates a new network namespace and returns a handle to it. +func New() (ns NsHandle, err error) { + if err := syscall.Unshare(CLONE_NEWNET); err != nil { + return -1, err + } + return Get() +} + +// Get gets a handle to the current threads network namespace. +func Get() (NsHandle, error) { + return GetFromThread(os.Getpid(), syscall.Gettid()) +} + +// GetFromPath gets a handle to a network namespace +// identified by the path +func GetFromPath(path string) (NsHandle, error) { + fd, err := syscall.Open(path, syscall.O_RDONLY, 0) + if err != nil { + return -1, err + } + return NsHandle(fd), nil +} + +// GetFromName gets a handle to a named network namespace such as one +// created by `ip netns add`. +func GetFromName(name string) (NsHandle, error) { + return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name)) +} + +// GetFromPid gets a handle to the network namespace of a given pid. +func GetFromPid(pid int) (NsHandle, error) { + return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid)) +} + +// GetFromThread gets a handle to the network namespace of a given pid and tid. +func GetFromThread(pid, tid int) (NsHandle, error) { + return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)) +} + +// GetFromDocker gets a handle to the network namespace of a docker container. +// Id is prefixed matched against the running docker containers, so a short +// identifier can be used as long as it isn't ambiguous. +func GetFromDocker(id string) (NsHandle, error) { + pid, err := getPidForContainer(id) + if err != nil { + return -1, err + } + return GetFromPid(pid) +} + +// borrowed from docker/utils/utils.go +func findCgroupMountpoint(cgroupType string) (string, error) { + output, err := ioutil.ReadFile("/proc/mounts") + if err != nil { + return "", err + } + + // /proc/mounts has 6 fields per line, one mount per line, e.g. + // cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, " ") + if len(parts) == 6 && parts[2] == "cgroup" { + for _, opt := range strings.Split(parts[3], ",") { + if opt == cgroupType { + return parts[1], nil + } + } + } + } + + return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) +} + +// Returns the relative path to the cgroup docker is running in. +// borrowed from docker/utils/utils.go +// modified to get the docker pid instead of using /proc/self +func getThisCgroup(cgroupType string) (string, error) { + dockerpid, err := ioutil.ReadFile("/var/run/docker.pid") + if err != nil { + return "", err + } + result := strings.Split(string(dockerpid), "\n") + if len(result) == 0 || len(result[0]) == 0 { + return "", fmt.Errorf("docker pid not found in /var/run/docker.pid") + } + pid, err := strconv.Atoi(result[0]) + + output, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) + if err != nil { + return "", err + } + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, ":") + // any type used by docker should work + if parts[1] == cgroupType { + return parts[2], nil + } + } + return "", fmt.Errorf("cgroup '%s' not found in /proc/%d/cgroup", cgroupType, pid) +} + +// Returns the first pid in a container. +// borrowed from docker/utils/utils.go +// modified to only return the first pid +// modified to glob with id +// modified to search for newer docker containers +func getPidForContainer(id string) (int, error) { + pid := 0 + + // memory is chosen randomly, any cgroup used by docker works + cgroupType := "memory" + + cgroupRoot, err := findCgroupMountpoint(cgroupType) + if err != nil { + return pid, err + } + + cgroupThis, err := getThisCgroup(cgroupType) + if err != nil { + return pid, err + } + + id += "*" + + attempts := []string{ + filepath.Join(cgroupRoot, cgroupThis, id, "tasks"), + // With more recent lxc versions use, cgroup will be in lxc/ + filepath.Join(cgroupRoot, cgroupThis, "lxc", id, "tasks"), + // With more recent dockee, cgroup will be in docker/ + filepath.Join(cgroupRoot, cgroupThis, "docker", id, "tasks"), + } + + var filename string + for _, attempt := range attempts { + filenames, _ := filepath.Glob(attempt) + if len(filenames) > 1 { + return pid, fmt.Errorf("Ambiguous id supplied: %v", filenames) + } else if len(filenames) == 1 { + filename = filenames[0] + break + } + } + + if filename == "" { + return pid, fmt.Errorf("Unable to find container: %v", id[:len(id)-1]) + } + + output, err := ioutil.ReadFile(filename) + if err != nil { + return pid, err + } + + result := strings.Split(string(output), "\n") + if len(result) == 0 || len(result[0]) == 0 { + return pid, fmt.Errorf("No pid found for container") + } + + pid, err = strconv.Atoi(result[0]) + if err != nil { + return pid, fmt.Errorf("Invalid pid '%s': %s", result[0], err) + } + + return pid, nil +} diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go new file mode 100644 index 00000000000..1d769bb1514 --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_386.go @@ -0,0 +1,7 @@ +// +build linux,386 + +package netns + +const ( + SYS_SETNS = 346 +) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go new file mode 100644 index 00000000000..b124666f18a --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_amd64.go @@ -0,0 +1,7 @@ +// +build linux,amd64 + +package netns + +const ( + SYS_SETNS = 308 +) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go new file mode 100644 index 00000000000..9c74eb55322 --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm.go @@ -0,0 +1,7 @@ +// +build linux,arm + +package netns + +const ( + SYS_SETNS = 375 +) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go new file mode 100644 index 00000000000..741a30207ad --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_arm64.go @@ -0,0 +1,7 @@ +// +build linux,arm64 + +package netns + +const ( + SYS_SETNS = 268 +) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go new file mode 100644 index 00000000000..c49eba5ee5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_ppc64le.go @@ -0,0 +1,7 @@ +// +build linux,ppc64le + +package netns + +const ( + SYS_SETNS = 350 +) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go new file mode 100644 index 00000000000..cc13e62665f --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_linux_s390x.go @@ -0,0 +1,7 @@ +// +build linux,s390x + +package netns + +const ( + SYS_SETNS = 339 +) diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go new file mode 100644 index 00000000000..e51981cc1bf --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_test.go @@ -0,0 +1,66 @@ +package netns + +import ( + "runtime" + "sync" + "testing" +) + +func TestGetNewSetDelete(t *testing.T) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + origns, err := Get() + if err != nil { + t.Fatal(err) + } + newns, err := New() + if err != nil { + t.Fatal(err) + } + if origns.Equal(newns) { + t.Fatal("New ns failed") + } + if err := Set(origns); err != nil { + t.Fatal(err) + } + newns.Close() + if newns.IsOpen() { + t.Fatal("newns still open after close", newns) + } + ns, err := Get() + if err != nil { + t.Fatal(err) + } + if !ns.Equal(origns) { + t.Fatal("Reset ns failed", origns, newns, ns) + } +} + +func TestNone(t *testing.T) { + ns := None() + if ns.IsOpen() { + t.Fatal("None ns is open", ns) + } +} + +func TestThreaded(t *testing.T) { + ncpu := runtime.GOMAXPROCS(-1) + if ncpu < 2 { + t.Skip("-cpu=2 or larger required") + } + + // Lock this thread simply to ensure other threads get used. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + wg := &sync.WaitGroup{} + for i := 0; i < ncpu; i++ { + wg.Add(1) + go func() { + defer wg.Done() + TestGetNewSetDelete(t) + }() + } + wg.Wait() +} diff --git a/Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go new file mode 100644 index 00000000000..b2edc565bde --- /dev/null +++ b/Godeps/_workspace/src/github.com/vishvananda/netns/netns_unspecified.go @@ -0,0 +1,35 @@ +// +build !linux + +package netns + +import ( + "errors" +) + +var ( + ErrNotImplemented = errors.New("not implemented") +) + +func Set(ns NsHandle) (err error) { + return ErrNotImplemented +} + +func New() (ns NsHandle, err error) { + return -1, ErrNotImplemented +} + +func Get() (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromName(name string) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromPid(pid int) (NsHandle, error) { + return -1, ErrNotImplemented +} + +func GetFromDocker(id string) (NsHandle, error) { + return -1, ErrNotImplemented +}