From bf33546a2d30bf1bf523bdc30256aabc1236478c Mon Sep 17 00:00:00 2001 From: Eugene Yakubovich Date: Tue, 14 Apr 2015 15:35:08 -0700 Subject: [PATCH] net: stage1 gc will clean up networking --- Documentation/pod-lifecycle.md | 4 +- networking/net.go | 122 --------------- networking/net/net.go | 27 +--- networking/net_plugin.go | 24 +-- networking/netinfo/netinfo.go | 9 +- networking/networking.go | 268 +++++++++++++-------------------- networking/podenv.go | 232 ++++++++++++++++++++++++++++ stage0/gc.go | 9 +- stage1/gc/gc.go | 54 ++++++- stage1/init/init.go | 10 +- 10 files changed, 416 insertions(+), 343 deletions(-) delete mode 100644 networking/net.go create mode 100644 networking/podenv.go diff --git a/Documentation/pod-lifecycle.md b/Documentation/pod-lifecycle.md index 0a5e019..a3a9c1c 100644 --- a/Documentation/pod-lifecycle.md +++ b/Documentation/pod-lifecycle.md @@ -51,7 +51,7 @@ After preparation completes, while still holding the exclusive lock (the lock is `rkt run` transitions directly from #Prepare to #Run by renaming `$var/prepare/$uuid` to `$var/run/$uuid`, entirely skipping the #Prepared phase. -Should #Prepare fail or be interrupted, `$var/prepare/$uuid` will be left in an unlocked state. Any directory in `$var/prepare` in an unlocked state is considered a failed prepare. `rkt gc` identifies failed prepares in need of cleanup by trying to acquire a shared lock on all directories in `$var/prepare`, renaming successfully locked directories to `$var/garbage` where they are then deleted. +Should #Prepare fail or be interrupted, `$var/prepare/$uuid` will be left in an unlocked state. Any directory in `$var/prepare` in an unlocked state is considered a failed prepare. `rkt gc` identifies failed prepares in need of clean up by trying to acquire a shared lock on all directories in `$var/prepare`, renaming successfully locked directories to `$var/garbage` where they are then deleted. ## Prepared @@ -91,7 +91,7 @@ Marked exited pods dwell in the `$var/exited-garbage` directory for a grace peri A side-effect of the rename operation responsible for moving a pod from `$var/run` to `$var/exited-garbage` is an update to the pod directory's change time. The sweep operation takes advantage of this in honoring the necessary grace period before discarding exited pods. This grace period currently defaults to 30 minutes, and may be explicitly specified using the `--grace-period duration` flag with `rkt gc`. Note that this grace period begins from the time a pod was marked by `rkt gc`, not when the pod exited. A pod becomes eligible for marking upon exit, but will not become marked until a subsequent `rkt gc` is performed. -The change times of all directories found in `$var/exited-garbage` are compared against the current time. Directories having sufficiently old change times are locked exclusively and cleaned up. If a lock acquisition fails, the directory is skipped. Failed exclusive lock acquisitions may occur if the garbage pod is currently being accessed via `rkt status`, or deleted by a concurrent `rkt gc`, for example. The skipped pods will be revisited on a subsequent `rkt gc` invocation's sweep pass. During the cleanup, pod's stage1 gc entry point is first executed. This gives a stage1 to cleanup anything related to the environment shared between containers. Currently, this cleans up the private networking artifacts. After the completion of the gc entrypoint, the pod directory is recursively deleted. +The change times of all directories found in `$var/exited-garbage` are compared against the current time. Directories having sufficiently old change times are locked exclusively and cleaned up. If a lock acquisition fails, the directory is skipped. Failed exclusive lock acquisitions may occur if the garbage pod is currently being accessed via `rkt status`, or deleted by a concurrent `rkt gc`, for example. The skipped pods will be revisited on a subsequent `rkt gc` invocation's sweep pass. During the cleanup, the pod's stage1 gc entry point is first executed. This gives the stage1 a chance to clean up anything related to the environment shared between containers. The default stage1 uses the gc entrypoint to clean up the private networking artifacts. After the completion of the gc entrypoint, the pod directory is recursively deleted. ## Pulse diff --git a/networking/net.go b/networking/net.go deleted file mode 100644 index 4fb0689..0000000 --- a/networking/net.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2015 CoreOS, 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. - -package networking - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "path" - "sort" - - "github.com/coreos/rkt/common" - rktnet "github.com/coreos/rkt/networking/net" -) - -// Net encodes a network plugin. -type Net struct { - rktnet.Net - args string -} - -// Absolute path where users place their net configs -const UserNetPath = "/etc/rkt/net.d" - -// Default net path relative to stage1 root -const DefaultNetPath = "etc/rkt/net.d/99-default.conf" - -func listFiles(dir string) ([]string, error) { - dirents, err := ioutil.ReadDir(dir) - switch { - case err == nil: - case os.IsNotExist(err): - return nil, nil - default: - return nil, err - } - - files := []string{} - for _, dent := range dirents { - if dent.IsDir() { - continue - } - - files = append(files, dent.Name()) - } - - return files, nil -} - -func netExists(nets []Net, name string) bool { - for _, n := range nets { - if n.Name == name { - return true - } - } - return false -} - -func loadUserNets() ([]Net, error) { - files, err := listFiles(UserNetPath) - if err != nil { - return nil, err - } - - sort.Strings(files) - - nets := make([]Net, 0, len(files)) - - for _, filename := range files { - filepath := path.Join(UserNetPath, filename) - n := Net{} - if err := rktnet.LoadNet(filepath, &n); err != nil { - return nil, fmt.Errorf("error loading %v: %v", filepath, err) - } - - if n.Name == "default" { - log.Printf(`Overriding "default" network with %v`, filename) - } - - if netExists(nets, n.Name) { - // "default" is slightly special - log.Printf("%q network already defined, ignoring %v", filename) - continue - } - - nets = append(nets, n) - } - - return nets, nil -} - -// Loads nets specified by user and default one from stage1 -func (e *podEnv) loadNets() ([]Net, error) { - nets, err := loadUserNets() - if err != nil { - return nil, err - } - - if !netExists(nets, "default") { - defPath := path.Join(common.Stage1RootfsPath(e.rktRoot), DefaultNetPath) - defNet := Net{} - if err := rktnet.LoadNet(defPath, &defNet); err != nil { - return nil, fmt.Errorf("error loading net: %v", err) - } - nets = append(nets, defNet) - } - - return nets, nil -} diff --git a/networking/net/net.go b/networking/net/net.go index 856522d..757a1df 100644 --- a/networking/net/net.go +++ b/networking/net/net.go @@ -19,15 +19,13 @@ import ( "io/ioutil" gonet "net" "os" - "reflect" ) // Net describes a network. type Net struct { - Filename string - Name string `json:"name,omitempty"` - Type string `json:"type,omitempty"` - IPAM struct { + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + IPAM struct { Type string `json:"type,omitempty"` } `json:"ipam,omitempty"` } @@ -39,24 +37,7 @@ func LoadNet(path string, n interface{}) error { return err } - if err = json.Unmarshal(c, n); err != nil { - return err - } - - // populate n.Filename if exists - v := reflect.ValueOf(n) - if v.Kind() == reflect.Ptr { - v = v.Elem() - if v.Kind() == reflect.Struct { - if fn := v.FieldByName("Filename"); fn.IsValid() { - if fn.Type().Kind() == reflect.String && fn.CanSet() { - fn.SetString(path) - } - } - } - } - - return nil + return json.Unmarshal(c, n) } // this is what net plugin returns to rkt diff --git a/networking/net_plugin.go b/networking/net_plugin.go index 39158c0..689bcf3 100644 --- a/networking/net_plugin.go +++ b/networking/net_plugin.go @@ -32,22 +32,22 @@ import ( const UserNetPluginsPath = "/usr/lib/rkt/plugins/net" const BuiltinNetPluginsPath = "usr/lib/rkt/plugins/net" -func (e *podEnv) netPluginAdd(n *Net, netns, args, ifName string) (ip, hostIP net.IP, err error) { - output, err := e.execNetPlugin("ADD", n, netns, args, ifName) +func (e *podEnv) netPluginAdd(n *activeNet, netns string) (ip, hostIP net.IP, err error) { + output, err := e.execNetPlugin("ADD", n, netns) if err != nil { return nil, nil, err } ifConf := rktnet.IfConfig{} if err = json.Unmarshal(output, &ifConf); err != nil { - return nil, nil, fmt.Errorf("error parsing %q output: %v", n.Name, err) + return nil, nil, fmt.Errorf("error parsing %q output: %v", n.Conf.Name, err) } return ifConf.IP, ifConf.HostIP, nil } -func (e *podEnv) netPluginDel(n *Net, netns, args, ifName string) error { - _, err := e.execNetPlugin("DEL", n, netns, args, ifName) +func (e *podEnv) netPluginDel(n *activeNet, netns string) error { + _, err := e.execNetPlugin("DEL", n, netns) return err } @@ -80,20 +80,20 @@ func envVars(vars [][2]string) []string { return env } -func (e *podEnv) execNetPlugin(cmd string, n *Net, netns, args, ifName string) ([]byte, error) { - pluginPath := e.findNetPlugin(n.Type) +func (e *podEnv) execNetPlugin(cmd string, n *activeNet, netns string) ([]byte, error) { + pluginPath := e.findNetPlugin(n.Conf.Type) if pluginPath == "" { - return nil, fmt.Errorf("Could not find plugin %q", n.Type) + return nil, fmt.Errorf("Could not find plugin %q", n.Conf.Type) } vars := [][2]string{ {"RKT_NETPLUGIN_COMMAND", cmd}, {"RKT_NETPLUGIN_PODID", e.podID.String()}, {"RKT_NETPLUGIN_NETNS", netns}, - {"RKT_NETPLUGIN_ARGS", args}, - {"RKT_NETPLUGIN_IFNAME", ifName}, - {"RKT_NETPLUGIN_NETNAME", n.Name}, - {"RKT_NETPLUGIN_NETCONF", n.Filename}, + {"RKT_NETPLUGIN_ARGS", n.Runtime.Args}, + {"RKT_NETPLUGIN_IFNAME", n.Runtime.IfName}, + {"RKT_NETPLUGIN_NETNAME", n.Conf.Name}, + {"RKT_NETPLUGIN_NETCONF", n.Runtime.ConfPath}, {"RKT_NETPLUGIN_IPAMPATH", strings.Join(e.pluginPaths(), ":")}, } diff --git a/networking/netinfo/netinfo.go b/networking/netinfo/netinfo.go index 6323550..41ceea1 100644 --- a/networking/netinfo/netinfo.go +++ b/networking/netinfo/netinfo.go @@ -16,6 +16,7 @@ package netinfo import ( "encoding/json" + "net" "os" "path/filepath" "syscall" @@ -24,9 +25,11 @@ import ( const filename = "net-info.json" type NetInfo struct { - NetName string `json:"netName"` - IfName string `json:"ifName"` - IP string `json:"ip"` + NetName string `json:"netName"` + ConfPath string `json:"netConf"` + IfName string `json:"ifName"` + IP net.IP `json:"ip"` + Args string `json:"args"` } func LoadAt(cdirfd int) ([]NetInfo, error) { diff --git a/networking/networking.go b/networking/networking.go index 689e5da..ececdaf 100644 --- a/networking/networking.go +++ b/networking/networking.go @@ -16,16 +16,15 @@ package networking import ( "fmt" - "io" "log" "net" "os" - "path/filepath" "syscall" "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" "github.com/coreos/rkt/Godeps/_workspace/src/github.com/vishvananda/netlink" + rktnet "github.com/coreos/rkt/networking/net" "github.com/coreos/rkt/networking/netinfo" "github.com/coreos/rkt/networking/util" ) @@ -35,21 +34,6 @@ const ( selfNetNS = "/proc/self/ns/net" ) -type activeNet struct { - Net - ifName string - ip net.IP - hostIP net.IP // kludge for default network -} - -// "base" struct that's populated from the beginning -// describing the environment in which the pod -// is running in -type podEnv struct { - rktRoot string - podID types.UUID -} - // ForwardedPort describes a port that will be // forwarded (mapped) from the host to the pod type ForwardedPort struct { @@ -62,19 +46,14 @@ type ForwardedPort struct { type Networking struct { podEnv - MetadataIP net.IP - HostIP net.IP - - podID types.UUID - hostNS *os.File - podNS *os.File - podNSPath string - nets []activeNet + hostNS *os.File + nets []activeNet } -// Setup produces a Networking object for a given pod ID. +// Setup creates a new networking namespace and executes network +// plugins to setup private networking. It returns in the new pod +// namespace func Setup(rktRoot string, podID types.UUID, fps []ForwardedPort) (*Networking, error) { - var err error n := Networking{ podEnv: podEnv{ rktRoot: rktRoot, @@ -82,69 +61,115 @@ func Setup(rktRoot string, podID types.UUID, fps []ForwardedPort) (*Networking, }, } - defer func() { - // cleanup on error - if err != nil { - n.Teardown() - } - }() - - if n.hostNS, n.podNS, err = basicNetNS(); err != nil { + hostNS, podNS, err := basicNetNS() + if err != nil { return nil, err } // we're in podNS! + n.hostNS = hostNS - n.podNSPath = filepath.Join(rktRoot, "netns") - if err = bindMountFile(selfNetNS, n.podNSPath); err != nil { + nspath := n.podNSPath() + + if err = bindMountFile(selfNetNS, nspath); err != nil { return nil, err } - nets, err := n.loadNets() + defer func() { + if err != nil { + if err := syscall.Unmount(nspath, 0); err != nil { + log.Printf("Error unmounting %q: %v", nspath, err) + } + } + }() + + n.nets, err = n.loadNets() if err != nil { return nil, fmt.Errorf("error loading network definitions: %v", err) } - err = withNetNS(n.podNS, n.hostNS, func() error { - n.nets, err = n.setupNets(n.podNSPath, nets) - if err != nil { + err = withNetNS(podNS, hostNS, func() error { + if err := n.setupNets(n.nets); err != nil { return err } - - if len(n.nets) == 0 { - return fmt.Errorf("no nets successfully configured") - } - - // last net is the default - n.MetadataIP = n.nets[len(n.nets)-1].ip - n.HostIP = n.nets[len(n.nets)-1].hostIP - - return n.forwardPorts(fps, n.MetadataIP) + return n.forwardPorts(fps, n.GetDefaultIP()) }) if err != nil { return nil, err } - if err = n.saveNetInfo(); err != nil { - return nil, err - } - return &n, nil } +// Load creates the Networking object from saved state. +// Assumes the current netns is that of the host. +func Load(rktRoot string, podID *types.UUID) (*Networking, error) { + // the current directory is pod root + pdirfd, err := syscall.Open(rktRoot, syscall.O_RDONLY|syscall.O_DIRECTORY, 0) + if err != nil { + return nil, fmt.Errorf("Failed to open pod root directory (%v): %v", rktRoot, err) + } + defer syscall.Close(pdirfd) + + nis, err := netinfo.LoadAt(pdirfd) + if err != nil { + return nil, err + } + + hostNS, err := os.Open(selfNetNS) + if err != nil { + return nil, err + } + + nets := []activeNet{} + for _, ni := range nis { + conf := &rktnet.Net{} + if err := rktnet.LoadNet(ni.ConfPath, conf); err != nil { + if !os.IsNotExist(err) { + log.Printf("Error loading %q: %v; ignoring", ni.ConfPath, err) + } + continue + } + + // make a copy of ni to make it a unique object as it's saved via ptr + rti := ni + nets = append(nets, activeNet{ + Conf: conf, + Runtime: &rti, + }) + } + + return &Networking{ + podEnv: podEnv{ + rktRoot: rktRoot, + podID: *podID, + }, + hostNS: hostNS, + nets: nets, + }, nil +} + +func (n *Networking) GetDefaultIP() net.IP { + if len(n.nets) == 0 { + return nil + } + return n.nets[len(n.nets)-1].Runtime.IP +} + +func (n *Networking) GetDefaultHostIP() net.IP { + if len(n.nets) == 0 { + return nil + } + return n.nets[len(n.nets)-1].hostIP +} + // Teardown cleans up a produced Networking object. func (n *Networking) Teardown() { // Teardown everything in reverse order of setup. - // This is called during error cases as well, so - // not everything may be setup. - // N.B. better to keep going in case of errors - // to get as much cleaned up as possible. + // This should be indempotent -- be tolerant of + // missing stuff - if n.podNS == nil || n.hostNS == nil { - return - } - - if err := n.EnterHostNS(); err != nil { - log.Print(err) + if err := n.enterHostNS(); err != nil { + log.Printf("Error switching to host netns: %v", err) return } @@ -152,14 +177,13 @@ func (n *Networking) Teardown() { log.Printf("Error removing forwarded ports: %v", err) } - n.teardownNets(n.podNSPath, n.nets) + n.teardownNets(n.nets) - if n.podNSPath == "" { - return - } - - if err := syscall.Unmount(n.podNSPath, 0); err != nil { - log.Printf("Error unmounting %q: %v", n.podNSPath, err) + if err := syscall.Unmount(n.podNSPath(), 0); err != nil { + // if already unmounted, umount(2) returns EINVAL + if !os.IsNotExist(err) && err != syscall.EINVAL { + log.Printf("Error unmounting %q: %v", n.podNSPath(), err) + } } } @@ -181,88 +205,17 @@ func basicNetNS() (hostNS, podNS *os.File, err error) { return } -// EnterHostNS moves into the host's network namespace. -func (n *Networking) EnterHostNS() error { +// enterHostNS moves into the host's network namespace. +func (n *Networking) enterHostNS() error { return util.SetNS(n.hostNS, syscall.CLONE_NEWNET) } -// EnterPodNS moves into the pod's network namespace. -func (n *Networking) EnterPodNS() error { - return util.SetNS(n.podNS, syscall.CLONE_NEWNET) -} - -func (e *podEnv) netDir() string { - return filepath.Join(e.rktRoot, "net") -} - -func (e *podEnv) setupNets(netns string, nets []Net) ([]activeNet, error) { - err := os.MkdirAll(e.netDir(), 0755) - if err != nil { - return nil, err - } - - active := []activeNet{} - - for i, nt := range nets { - log.Printf("Setup: executing net-plugin %v", nt.Type) - - an := activeNet{ - Net: nt, - ifName: fmt.Sprintf(ifnamePattern, i), - } - - if an.Filename, err = copyFileToDir(nt.Filename, e.netDir()); err != nil { - err = fmt.Errorf("error copying %q to %q: %v", nt.Filename, e.netDir(), err) - break - } - - an.ip, an.hostIP, err = e.netPluginAdd(&nt, netns, nt.args, an.ifName) - if err != nil { - err = fmt.Errorf("error adding network %q: %v", nt.Name, err) - break - } - - active = append(active, an) - } - - if err != nil { - e.teardownNets(netns, active) - return nil, err - } - - return active, nil -} - -func (e *podEnv) teardownNets(netns string, nets []activeNet) { - for i := len(nets) - 1; i >= 0; i-- { - nt := nets[i] - - log.Printf("Teardown: executing net-plugin %v", nt.Type) - - err := e.netPluginDel(&nt.Net, netns, nt.args, nt.ifName) - if err != nil { - log.Printf("Error deleting %q: %v", nt.Name, err) - } - - // Delete the conf file to signal that the network was - // torn down (or at least attempted to) - if err = os.Remove(nt.Filename); err != nil { - log.Printf("Error deleting %q: %v", nt.Filename, err) - } - } -} - -// saveNetInfo writes out the info about active nets +// Save writes out the info about active nets // for "rkt list" and friends to display -func (e *Networking) saveNetInfo() error { +func (e *Networking) Save() error { nis := []netinfo.NetInfo{} for _, n := range e.nets { - ni := netinfo.NetInfo{ - NetName: n.Name, - IfName: n.ifName, - IP: n.ip.String(), - } - nis = append(nis, ni) + nis = append(nis, *n.Runtime) } return netinfo.Save(e.rktRoot, nis) @@ -338,22 +291,3 @@ func bindMountFile(src, dst string) error { return syscall.Mount(src, dst, "none", syscall.MS_BIND, "") } - -func copyFileToDir(src, dstdir string) (string, error) { - dst := filepath.Join(dstdir, filepath.Base(src)) - - s, err := os.Open(src) - if err != nil { - return "", err - } - defer s.Close() - - d, err := os.Create(dst) - if err != nil { - return "", err - } - defer d.Close() - - _, err = io.Copy(d, s) - return dst, err -} diff --git a/networking/podenv.go b/networking/podenv.go new file mode 100644 index 0000000..46f7a48 --- /dev/null +++ b/networking/podenv.go @@ -0,0 +1,232 @@ +// Copyright 2015 CoreOS, 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. + +package networking + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "net" + "os" + "path" + "path/filepath" + "sort" + + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" + + "github.com/coreos/rkt/common" + rktnet "github.com/coreos/rkt/networking/net" + "github.com/coreos/rkt/networking/netinfo" +) + +const ( + // Absolute path where users place their net configs + UserNetPath = "/etc/rkt/net.d" + + // Default net path relative to stage1 root + DefaultNetPath = "etc/rkt/net.d/99-default.conf" +) + +// "base" struct that's populated from the beginning +// describing the environment in which the pod +// is running in +type podEnv struct { + rktRoot string + podID types.UUID +} + +type activeNet struct { + Conf *rktnet.Net + Runtime *netinfo.NetInfo + hostIP net.IP // kludge for default network +} + +// Loads nets specified by user and default one from stage1 +func (e *podEnv) loadNets() ([]activeNet, error) { + nets, err := loadUserNets() + if err != nil { + return nil, err + } + + if !netExists(nets, "default") { + defPath := path.Join(common.Stage1RootfsPath(e.rktRoot), DefaultNetPath) + n, err := loadNet(defPath) + if err != nil { + return nil, err + } + nets = append(nets, *n) + } + + return nets, nil +} + +func (e *podEnv) podNSPath() string { + return filepath.Join(e.rktRoot, "netns") +} + +func (e *podEnv) netDir() string { + return filepath.Join(e.rktRoot, "net") +} + +func (e *podEnv) setupNets(nets []activeNet) error { + err := os.MkdirAll(e.netDir(), 0755) + if err != nil { + return err + } + + i := 0 + defer func() { + if err != nil { + e.teardownNets(nets[:i]) + } + }() + + nspath := e.podNSPath() + + n := activeNet{} + for i, n = range nets { + log.Printf("Setup: executing net-plugin %v", n.Conf.Type) + + n.Runtime.IfName = fmt.Sprintf(ifnamePattern, i) + if n.Runtime.ConfPath, err = copyFileToDir(n.Runtime.ConfPath, e.netDir()); err != nil { + return fmt.Errorf("error copying %q to %q: %v", n.Runtime.ConfPath, e.netDir(), err) + } + + n.Runtime.IP, n.hostIP, err = e.netPluginAdd(&n, nspath) + if err != nil { + return fmt.Errorf("error adding network %q: %v", n.Conf.Name, err) + } + } + + return nil +} + +func (e *podEnv) teardownNets(nets []activeNet) { + nspath := e.podNSPath() + + for i := len(nets) - 1; i >= 0; i-- { + log.Printf("Teardown: executing net-plugin %v", nets[i].Conf.Type) + + err := e.netPluginDel(&nets[i], nspath) + if err != nil { + log.Printf("Error deleting %q: %v", nets[i].Conf.Name, err) + } + + // Delete the conf file to signal that the network was + // torn down (or at least attempted to) + if err = os.Remove(nets[i].Runtime.ConfPath); err != nil { + log.Printf("Error deleting %q: %v", nets[i].Runtime.ConfPath, err) + } + } +} + +func listFiles(dir string) ([]string, error) { + dirents, err := ioutil.ReadDir(dir) + switch { + case err == nil: + case os.IsNotExist(err): + return nil, nil + default: + return nil, err + } + + files := []string{} + for _, dent := range dirents { + if dent.IsDir() { + continue + } + + files = append(files, dent.Name()) + } + + return files, nil +} + +func netExists(nets []activeNet, name string) bool { + for _, n := range nets { + if n.Conf.Name == name { + return true + } + } + return false +} + +func loadNet(filepath string) (*activeNet, error) { + n := &rktnet.Net{} + if err := rktnet.LoadNet(filepath, n); err != nil { + return nil, fmt.Errorf("error loading %v: %v", filepath, err) + } + + return &activeNet{ + Conf: n, + Runtime: &netinfo.NetInfo{ + NetName: n.Name, + ConfPath: filepath, + }, + }, nil +} + +func copyFileToDir(src, dstdir string) (string, error) { + dst := filepath.Join(dstdir, filepath.Base(src)) + + s, err := os.Open(src) + if err != nil { + return "", err + } + defer s.Close() + + d, err := os.Create(dst) + if err != nil { + return "", err + } + defer d.Close() + + _, err = io.Copy(d, s) + return dst, err +} + +func loadUserNets() ([]activeNet, error) { + files, err := listFiles(UserNetPath) + if err != nil { + return nil, err + } + + sort.Strings(files) + + nets := make([]activeNet, 0, len(files)) + + for _, filename := range files { + filepath := path.Join(UserNetPath, filename) + n, err := loadNet(filepath) + if err != nil { + return nil, err + } + + // "default" is slightly special + if n.Conf.Name == "default" { + log.Printf(`Overriding "default" network with %v`, filename) + } + + if netExists(nets, n.Conf.Name) { + log.Printf("%q network already defined, ignoring %v", n.Conf.Name, filename) + continue + } + + nets = append(nets, *n) + } + + return nets, nil +} diff --git a/stage0/gc.go b/stage0/gc.go index cbd4585..e768e65 100644 --- a/stage0/gc.go +++ b/stage0/gc.go @@ -1,4 +1,4 @@ -// Copyright 2014 CoreOS, Inc. +// Copyright 2015 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package stage0 import ( - "fmt" "log" "os" "os/exec" @@ -48,9 +47,5 @@ func GC(pdir string, uuid *types.UUID, debug bool) error { Stderr: os.Stderr, Dir: pdir, } - if err := c.Run(); err != nil { - return fmt.Errorf("error running gc: %v", err) - } - - return nil + return c.Run() } diff --git a/stage1/gc/gc.go b/stage1/gc/gc.go index fd908e4..e7bb8cc 100644 --- a/stage1/gc/gc.go +++ b/stage1/gc/gc.go @@ -17,10 +17,60 @@ package main import ( + "flag" "fmt" + "io/ioutil" + "log" "os" + "runtime" + + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" + + "github.com/coreos/rkt/networking" ) -func main() { - fmt.Fprintln(os.Stderr, "Hello from stage1 GC") +var ( + debug bool +) + +func init() { + flag.BoolVar(&debug, "debug", false, "Run in debug mode") + + // this ensures that main runs only on main thread (thread group leader). + // since namespace ops (unshare, setns) are done for a single thread, we + // must ensure that the goroutine does not jump from OS thread to thread + runtime.LockOSThread() +} + +func main() { + flag.Parse() + + if !debug { + log.SetOutput(ioutil.Discard) + } + + podID, err := types.NewUUID(flag.Arg(0)) + if err != nil { + fmt.Fprintln(os.Stderr, "UUID is missing or malformed") + os.Exit(1) + } + + if err := gcNetworking(podID); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func gcNetworking(podID *types.UUID) error { + n, err := networking.Load(".", podID) + switch { + case err == nil: + n.Teardown() + case os.IsNotExist(err): + // probably ran without --private-net + default: + return fmt.Errorf("Failed loading networking state: %v", err) + } + + return nil } diff --git a/stage1/init/init.go b/stage1/init/init.go index c3f298c..6ff0ab6 100644 --- a/stage1/init/init.go +++ b/stage1/init/init.go @@ -236,14 +236,14 @@ func stage1() int { } defer n.Teardown() - p.MetadataServiceURL = common.MetadataServicePublicURL(n.HostIP) - - if err = n.EnterPodNS(); err != nil { - fmt.Fprintf(os.Stderr, "Failed to switch to pod netns: %v\n", err) + if err = n.Save(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to save networking state %v\n", err) return 6 } - if err = registerPod(p, n.MetadataIP); err != nil { + p.MetadataServiceURL = common.MetadataServicePublicURL(n.GetDefaultHostIP()) + + if err = registerPod(p, n.GetDefaultIP()); err != nil { fmt.Fprintf(os.Stderr, "Failed to register pod: %v\n", err) return 6 }