From 0ff92f8e20edb46eb8a9d82b929e153bcdaa3044 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 6 Dec 2017 15:54:59 -0500 Subject: Add network namespaces to SQL state Signed-off-by: Matthew Heon Closes: #109 Approved by: mheon --- libpod/container.go | 5 ++++ libpod/networking.go | 37 ++++++++++++++++++-------- libpod/sql_state.go | 63 +++++++++++++++++++++++++++++++++++++------- libpod/sql_state_internal.go | 14 +++++++++- 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/libpod/container.go b/libpod/container.go index 12fd13f51..ada037531 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -12,6 +12,7 @@ import ( "time" "github.com/containerd/cgroups" + "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" "github.com/cri-o/ocicni/pkg/ocicni" @@ -100,6 +101,10 @@ type containerRuntimeInfo struct { OOMKilled bool `json:"oomKilled,omitempty"` // PID is the PID of a running container PID int `json:"pid,omitempty"` + // NetNSPath is the path of the container's network namespace + // Will only be set if config.CreateNetNS is true, or the container was + // told to join another container's network namespace + NetNS ns.NetNS // TODO: Save information about image used in container if one is used } diff --git a/libpod/networking.go b/libpod/networking.go index 893d6863a..f613ad5f8 100644 --- a/libpod/networking.go +++ b/libpod/networking.go @@ -3,11 +3,12 @@ package libpod import ( "github.com/containernetworking/plugins/pkg/ns" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // Get an OCICNI network config -func getPodNetwork(id, name, nsPath string, ports []ocicni.PortMappings) ocicni.PodNetwork { +func getPodNetwork(id, name, nsPath string, ports []ocicni.PortMapping) ocicni.PodNetwork { return ocicni.PodNetwork{ Name: name, Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces @@ -17,29 +18,31 @@ func getPodNetwork(id, name, nsPath string, ports []ocicni.PortMappings) ocicni. } } -// Create and configure a new network namespace -func (r *Runtime) createNetNS(id, name string, ports []ocicni.PortMapping) (n ns.NetNS, err error) { +// Create and configure a new network namespace for a container +func (r *Runtime) createNetNS(ctr *Container) (err error) { ns, err := ns.NewNS() if err != nil { - return nil, errors.Wrapf(err, "error creating network namespace %s", id) + return errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID()) } defer func() { if err != nil { if err2 := ns.Close(); err2 != nil { - logrus.Errorf("Error closing partially created network namespace %s: %v", id, err2) + logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err2) } } }() - podNetwork := getPodNetwork(id, name, ns.Path(), ports) + podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ns.Path(), ctr.config.PortMappings) if err := r.netPlugin.SetUpPod(podNetwork); err != nil { - return nil, errors.Wrapf(err, "error configuring network namespace %s", id) + return errors.Wrapf(err, "error configuring network namespace for container %s", ctr.ID()) } // TODO hostport mappings for forwarded ports - return ns, nil + ctr.state.NetNS = ns + + return nil } // Join an existing network namespace @@ -53,13 +56,25 @@ func joinNetNS(path string) (ns.NetNS, error) { } // Tear down a network namespace -func (r *Runtime) teardownNetNS(id, name string, ports []ocicni.PortMapping, ns ns.NetNS) error { +func (r *Runtime) teardownNetNS(ctr *Container) error { + if ctr.state.NetNS == nil { + // The container has no network namespace, we're set + return nil + } + // TODO hostport mappings for forwarded ports should be undone - podNetwork := getPodNetwork(id, name, ns.Path(), ports) + podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.PortMappings) + // The network may have already been torn down, so don't fail here, just log if err := r.netPlugin.TearDownPod(podNetwork); err != nil { - return errors.Wrapf(err, "failed to remove network namespace %s", id) + logrus.Errorf("Failed to tear down network namespace for container %s: %v", ctr.ID(), err) + } + + if err := ctr.state.NetNS.Close(); err != nil { + return errors.Wrapf(err, "error closing network namespace for container %s", ctr.ID()) } + ctr.state.NetNS = nil + return nil } diff --git a/libpod/sql_state.go b/libpod/sql_state.go index d0969a783..97df749e9 100644 --- a/libpod/sql_state.go +++ b/libpod/sql_state.go @@ -15,7 +15,7 @@ import ( // DBSchema is the current DB schema version // Increments every time a change is made to the database's tables -const DBSchema = 3 +const DBSchema = 4 // SQLState is a state implementation backed by a persistent SQLite3 database type SQLState struct { @@ -151,7 +151,8 @@ func (s *SQLState) Container(id string) (*Container, error) { containerState.FinishedTime, containerState.ExitCode, containerState.OomKilled, - containerState.Pid + containerState.Pid, + containerState.NetNSPath FROM containers INNER JOIN containerState ON containers.Id = containerState.Id @@ -186,7 +187,8 @@ func (s *SQLState) LookupContainer(idOrName string) (*Container, error) { containerState.FinishedTime, containerState.ExitCode, containerState.OomKilled, - containerState.Pid + containerState.Pid, + containerState.NetNSPath FROM containers INNER JOIN containerState ON containers.Id = containerState.Id @@ -270,7 +272,7 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );` addCtrState = `INSERT INTO containerState VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? );` ) @@ -297,6 +299,11 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { return errors.Wrapf(err, "error marshaling container %s port mappings to JSON", ctr.ID()) } + netNSPath := "" + if ctr.state.NetNS != nil { + netNSPath = ctr.state.NetNS.Path() + } + tx, err := s.db.Begin() if err != nil { return errors.Wrapf(err, "error beginning database transaction") @@ -342,7 +349,8 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { timeToSQL(ctr.state.FinishedTime), ctr.state.ExitCode, boolToSQL(ctr.state.OOMKilled), - ctr.state.PID) + ctr.state.PID, + netNSPath) if err != nil { return errors.Wrapf(err, "error adding container %s state to database", ctr.ID()) } @@ -381,7 +389,8 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { FinishedTime, ExitCode, OomKilled, - Pid + Pid, + NetNSPath FROM containerState WHERE ID=?;` var ( @@ -394,6 +403,7 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { exitCode int32 oomKilled int pid int + netNSPath string ) if !s.valid { @@ -414,7 +424,8 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { &finishedTimeString, &exitCode, &oomKilled, - &pid) + &pid, + &netNSPath) if err != nil { // The container may not exist in the database if err == sql.ErrNoRows { @@ -453,6 +464,32 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { } newState.FinishedTime = finishedTime + // Do we need to replace the container's netns? + if netNSPath != "" { + // Check if the container's old state has a good netns + if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() { + newState.NetNS = ctr.state.NetNS + } else { + // Tear down the existing namespace + if err := s.runtime.teardownNetNS(ctr); err != nil { + return err + } + + // Open the new network namespace + ns, err := joinNetNS(netNSPath) + if err != nil { + return errors.Wrapf(err, "error joining network namespace for container %s", ctr.ID()) + } + newState.NetNS = ns + } + } else { + // The container no longer has a network namespace + // Tear down the old one + if err := s.runtime.teardownNetNS(ctr); err != nil { + return err + } + } + // New state compiled successfully, swap it into the current state ctr.state = newState @@ -470,9 +507,15 @@ func (s *SQLState) SaveContainer(ctr *Container) error { FinishedTime=?, ExitCode=?, OomKilled=?, - Pid=? + Pid=?, + NetNSPath=? WHERE Id=?;` + netNSPath := "" + if ctr.state.NetNS != nil { + netNSPath = ctr.state.NetNS.Path() + } + if !s.valid { return ErrDBClosed } @@ -504,6 +547,7 @@ func (s *SQLState) SaveContainer(ctr *Container) error { ctr.state.ExitCode, boolToSQL(ctr.state.OOMKilled), ctr.state.PID, + netNSPath, ctr.ID()) if err != nil { return errors.Wrapf(err, "error updating container %s state in database", ctr.ID()) @@ -593,7 +637,8 @@ func (s *SQLState) AllContainers() ([]*Container, error) { containerState.FinishedTime, containerState.ExitCode, containerState.OomKilled, - containerState.Pid + containerState.Pid, + containerState.NetNSPath FROM containers INNER JOIN containerState ON containers.Id = containerState.Id diff --git a/libpod/sql_state_internal.go b/libpod/sql_state_internal.go index 1d1878b9d..e1ecc8ea6 100644 --- a/libpod/sql_state_internal.go +++ b/libpod/sql_state_internal.go @@ -200,6 +200,7 @@ func prepareDB(db *sql.DB) (err error) { ExitCode INTEGER NOT NULL, OomKilled INTEGER NOT NULL, Pid INTEGER NOT NULL, + NetNSPath TEXT NOT NULL, CHECK (State>0), CHECK (OomKilled IN (0, 1)), FOREIGN KEY (Id) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED @@ -296,6 +297,7 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir exitCode int32 oomKilled int pid int + netNSPath string ) err := row.Scan( @@ -323,7 +325,8 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir &finishedTimeString, &exitCode, &oomKilled, - &pid) + &pid, + &netNSPath) if err != nil { if err == sql.ErrNoRows { return nil, ErrNoSuchCtr @@ -394,6 +397,15 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir } ctr.state.FinishedTime = finishedTime + // Join the network namespace, if there is one + if netNSPath != "" { + netNS, err := joinNetNS(netNSPath) + if err != nil { + return nil, errors.Wrapf(err, "error joining network namespace for container %s", id) + } + ctr.state.NetNS = netNS + } + ctr.valid = true ctr.runtime = runtime -- cgit v1.2.3-54-g00ecf