diff --git a/networking/netinfo/netinfo.go b/networking/netinfo/netinfo.go new file mode 100644 index 0000000..6323550 --- /dev/null +++ b/networking/netinfo/netinfo.go @@ -0,0 +1,53 @@ +// 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 netinfo + +import ( + "encoding/json" + "os" + "path/filepath" + "syscall" +) + +const filename = "net-info.json" + +type NetInfo struct { + NetName string `json:"netName"` + IfName string `json:"ifName"` + IP string `json:"ip"` +} + +func LoadAt(cdirfd int) ([]NetInfo, error) { + fd, err := syscall.Openat(cdirfd, filename, syscall.O_RDONLY, 0) + if err != nil { + return nil, err + } + + f := os.NewFile(uintptr(fd), filename) + + info := []NetInfo{} + err = json.NewDecoder(f).Decode(&info) + return info, err +} + +func Save(root string, info []NetInfo) error { + f, err := os.Create(filepath.Join(root, filename)) + if err != nil { + return err + } + defer f.Close() + + return json.NewEncoder(f).Encode(info) +} diff --git a/networking/networking.go b/networking/networking.go index f509a93..471bd46 100644 --- a/networking/networking.go +++ b/networking/networking.go @@ -26,6 +26,7 @@ import ( "github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/schema/types" "github.com/coreos/rocket/Godeps/_workspace/src/github.com/vishvananda/netlink" + "github.com/coreos/rocket/networking/netinfo" "github.com/coreos/rocket/networking/util" ) @@ -105,6 +106,10 @@ func Setup(rktRoot string, contID types.UUID) (*Networking, error) { return nil, fmt.Errorf("no nets successfully setup") } + if err = n.saveNetInfo(); err != nil { + return nil, err + } + // last net is the default n.MetadataIP = n.nets[len(n.nets)-1].ip @@ -228,6 +233,22 @@ func (e *containerEnv) teardownNets(netns string, nets []activeNet) { } } +// saveNetInfo writes out the info about active nets +// for "rkt list" and friends to display +func (e *Networking) saveNetInfo() 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) + } + + return netinfo.Save(e.rktRoot, nis) +} + func newNetNS() (hostNS, childNS *os.File, err error) { defer func() { if err != nil { diff --git a/rkt/containers.go b/rkt/containers.go index 6bbaa71..e272957 100644 --- a/rkt/containers.go +++ b/rkt/containers.go @@ -28,6 +28,7 @@ import ( "github.com/coreos/rocket/Godeps/_workspace/src/code.google.com/p/go-uuid/uuid" "github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/schema/types" "github.com/coreos/rocket/common" + "github.com/coreos/rocket/networking/netinfo" "github.com/coreos/rocket/pkg/lock" ) @@ -36,7 +37,8 @@ import ( type container struct { *lock.DirLock uuid *types.UUID - createdByMe bool // true if we're the creator of this container (only the creator can xToPrepare or xToRun directly from preparing) + createdByMe bool // true if we're the creator of this container (only the creator can xToPrepare or xToRun directly from preparing) + nets []netinfo.NetInfo // list of networks (name, IP, iface) this container is using isEmbryo bool // directory starts as embryo before entering preparing state, serves as stage for acquiring lock before rename to prepare/. isPreparing bool // when locked at containers/prepare/$uuid the container is actively being prepared @@ -248,6 +250,18 @@ func getContainer(uuid string) (*container, error) { c.DirLock = l + if c.isRunning() { + cfd, err := c.Fd() + if err != nil { + return nil, fmt.Errorf("error acquiring container %v dir fd: %v", uuid, err) + } + c.nets, err = netinfo.LoadAt(cfd) + // ENOENT is ok -- assume running without --private-net + if err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("error opening container %v netinfo: %v", uuid, err) + } + } + return c, nil } diff --git a/rkt/list.go b/rkt/list.go index e92837a..e9923f0 100644 --- a/rkt/list.go +++ b/rkt/list.go @@ -18,9 +18,11 @@ package main import ( "fmt" + "strings" "github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/schema" common "github.com/coreos/rocket/common" + "github.com/coreos/rocket/networking/netinfo" ) var ( @@ -40,7 +42,7 @@ func init() { func runList(args []string) (exit int) { if !flagNoLegend { - fmt.Fprintf(tabOut, "UUID\tACI\tSTATE\n") + fmt.Fprintf(tabOut, "UUID\tACI\tSTATE\tNETWORKS\n") } if err := walkContainers(includeMostDirs, func(c *container) { @@ -68,7 +70,7 @@ func runList(args []string) (exit int) { app_zero = m.Apps[0].Name.String() } - fmt.Fprintf(tabOut, "%s\t%s\t%s\n", c.uuid, app_zero, c.getState()) + fmt.Fprintf(tabOut, "%s\t%s\t%s\t%s\n", c.uuid, app_zero, c.getState(), fmtNets(c.nets)) for i := 1; i < len(m.Apps); i++ { fmt.Fprintf(tabOut, "\t%s\n", m.Apps[i].Name.String()) } @@ -80,3 +82,12 @@ func runList(args []string) (exit int) { tabOut.Flush() return 0 } + +func fmtNets(nis []netinfo.NetInfo) string { + parts := []string{} + for _, ni := range nis { + // there will be IPv6 support soon so distinguish between v4 and v6 + parts = append(parts, fmt.Sprintf("%v:ip4=%v", ni.NetName, ni.IP)) + } + return strings.Join(parts, ", ") +} diff --git a/rkt/status.go b/rkt/status.go index 5436b87..147cd2b 100644 --- a/rkt/status.go +++ b/rkt/status.go @@ -74,6 +74,10 @@ func runStatus(args []string) (exit int) { func printStatus(c *container) error { stdout("state=%s", c.getState()) + if c.isRunning() { + stdout("networks=%s", fmtNets(c.nets)) + } + if !c.isEmbryo && !c.isPreparing && !c.isPrepared && !c.isAbortedPrepare && !c.isGarbage && !c.isGone { pid, err := c.getPID() if err != nil {