From e6be800ec633342aef656e0a2ed0bdc4519796d9 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Wed, 10 Jan 2018 13:49:38 -0500 Subject: Update DB to add new fields required for planned features Signed-off-by: Matthew Heon Closes: #209 Approved by: rhatdan --- libpod/container.go | 92 +++++++++++---- libpod/container_top.go | 2 +- libpod/options.go | 4 +- libpod/sql_state.go | 98 +++++++++++----- libpod/sql_state_internal.go | 271 ++++++++++++++++++++++++++++++------------- libpod/sql_state_test.go | 3 +- 6 files changed, 338 insertions(+), 132 deletions(-) (limited to 'libpod') diff --git a/libpod/container.go b/libpod/container.go index 66eb4aa2d..79fd5d42c 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -61,8 +61,8 @@ const ( artifactsDir = "artifacts" ) -// CGroupParent is the prefix to a cgroup path in libpod -var CGroupParent = "/libpod_parent" +// CgroupParent is the default prefix to a cgroup path in libpod +var CgroupParent = "/libpod_parent" // Container is a single OCI container type Container struct { @@ -78,6 +78,13 @@ type Container struct { runtime *Runtime } +// TODO fetch IP and Subnet Mask from networks once we have updated OCICNI +// TODO enable pod support +// TODO Add readonly support +// TODO add SHM size support +// TODO add shared namespace support +// TODO add cgroup parent support + // containerRuntimeInfo contains the current state of the container // It is stored on disk in a tmpfs and recreated on reboot type containerRuntimeInfo struct { @@ -107,7 +114,10 @@ type containerRuntimeInfo struct { // 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 + // IP address of container (if network namespace was created) + IPAddress string + // Subnet mask of container (if network namespace was created) + SubnetMask string } // ContainerConfig contains all information that was used to create the @@ -117,49 +127,76 @@ type ContainerConfig struct { Spec *spec.Spec `json:"spec"` ID string `json:"id"` Name string `json:"name"` + // Full ID of the pood the container belongs to + Pod string `json:"pod,omitempty"` + + // TODO consider breaking these subsections up into smaller structs + + // Storage Config // Information on the image used for the root filesystem RootfsImageID string `json:"rootfsImageID,omitempty"` RootfsImageName string `json:"rootfsImageName,omitempty"` - UseImageConfig bool `json:"useImageConfig"` + // Whether to mount volumes specified in the image + ImageVolumes bool `json:"imageVolumes"` + // Whether to make the container read only + ReadOnly bool `json:"readOnly"` + // Src path to be mounted on /dev/shm in container + ShmDir string `json:"ShmDir,omitempty"` + // Size of the container's SHM + ShmSize int64 `json:"shmSize"` + // Static directory for container content that will persist across + // reboot + StaticDir string `json:"staticDir"` + // Mounts list contains all additional mounts into the container rootfs + // These include the SHM mount + // These must be unmounted before the container's rootfs is unmounted + Mounts []string `json:"mounts,omitempty"` + + // Security Config // SELinux process label for container ProcessLabel string `json:"ProcessLabel,omitempty"` // SELinux mount label for root filesystem MountLabel string `json:"MountLabel,omitempty"` - // Src path to be mounted on /dev/shm in container - ShmDir string `json:"ShmDir,omitempty"` + // User and group to use in the container + // Can be specified by name or UID/GID + User string `json:"user"` + + // Namespace Config + // IDs of container to share namespaces with + // NetNsCtr conflicts with the CreateNetNS bool + IPCNsCtr string `json:"ipcNsCtr"` + MountNsCtr string `json:"mountNsCtr"` + NetNsCtr string `json:"netNsCtr"` + PIDNsCtr string `json:"pidNsCtr"` + UserNsCtr string `json:"userNsCtr"` + UTSNsCtr string `json:"utsNsCtr"` + + // Network Config // CreateNetNS indicates that libpod should create and configure a new // network namespace for the container + // This cannot be set if NetNsCtr is also set CreateNetNS bool `json:"createNetNS"` // PortMappings are the ports forwarded to the container's network // namespace // These are not used unless CreateNetNS is true - PortMappings []ocicni.PortMapping - // Static directory for container content that will persist across - // reboot - StaticDir string `json:"staticDir"` + PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"` + + // Misc Options // Whether to keep container STDIN open Stdin bool `json:"stdin,omitempty"` - // Pod the container belongs to - Pod string `json:"pod,omitempty"` // Labels is a set of key-value pairs providing additional information // about a container Labels map[string]string `json:"labels,omitempty"` - // Mounts list contains all additional mounts by the container runtime. - Mounts []string `json:"mounts,omitempty"` // StopSignal is the signal that will be used to stop the container StopSignal uint `json:"stopSignal,omitempty"` // StopTimeout is the signal that will be used to stop the container StopTimeout uint `json:"stopTimeout,omitempty"` - // Shared namespaces with container - SharedNamespaceCtr *string `json:"shareNamespacesWith,omitempty"` - SharedNamespaceMap map[string]string `json:"sharedNamespaces"` // Time container was created CreatedTime time.Time `json:"createdTime"` - // User/GID to use within the container - User string `json:"user"` + // Cgroup parent of the container + CgroupParent string `json:"cgroupParent"` - // TODO save log location here and pass into OCI code - // TODO allow overriding of log path + // TODO log options - logpath for plaintext, others for log drivers } // ContainerStater returns a string representation for users @@ -192,6 +229,12 @@ func (c *Container) Name() string { return c.config.Name } +// PodID returns the full ID of the pod the container belongs to, or "" if it +// does not belong to a pod +func (c *Container) PodID() string { + return c.config.Pod +} + // ShmDir returns the sources path to be mounted on /dev/shm in container func (c *Container) ShmDir() string { return c.config.ShmDir @@ -468,6 +511,9 @@ func newContainer(rspec *spec.Spec, lockDir string) (*Container, error) { deepcopier.Copy(rspec).To(ctr.config.Spec) ctr.config.CreatedTime = time.Now() + ctr.config.ShmSize = DefaultShmSize + ctr.config.CgroupParent = CgroupParent + // Path our lock file will reside at lockPath := filepath.Join(lockDir, ctr.config.ID) // Grab a lockfile at the given path @@ -679,7 +725,7 @@ func (c *Container) Init() (err error) { // With the spec complete, do an OCI create // TODO set cgroup parent in a sane fashion - if err := c.runtime.ociRuntime.createContainer(c, CGroupParent); err != nil { + if err := c.runtime.ociRuntime.createContainer(c, CgroupParent); err != nil { return err } @@ -1182,7 +1228,7 @@ func (c *Container) cleanupStorage() error { // CGroupPath returns a cgroups "path" for a given container. func (c *Container) CGroupPath() cgroups.Path { - return cgroups.StaticPath(filepath.Join(CGroupParent, fmt.Sprintf("libpod-conmon-%s", c.ID()))) + return cgroups.StaticPath(filepath.Join(CgroupParent, fmt.Sprintf("libpod-conmon-%s", c.ID()))) } // copyHostFileToRundir copies the provided file to the runtimedir diff --git a/libpod/container_top.go b/libpod/container_top.go index 7d6dad2b4..020773488 100644 --- a/libpod/container_top.go +++ b/libpod/container_top.go @@ -25,7 +25,7 @@ func (c *Container) GetContainerPids() ([]string, error) { // Gets the pids for a container without locking. should only be called from a func where // locking has already been established. func (c *Container) getContainerPids() ([]string, error) { - taskFile := filepath.Join("/sys/fs/cgroup/pids", CGroupParent, fmt.Sprintf("libpod-conmon-%s", c.ID()), c.ID(), "tasks") + taskFile := filepath.Join("/sys/fs/cgroup/pids", CgroupParent, fmt.Sprintf("libpod-conmon-%s", c.ID()), c.ID(), "tasks") logrus.Debug("reading pids from ", taskFile) content, err := ioutil.ReadFile(taskFile) if err != nil { diff --git a/libpod/options.go b/libpod/options.go index 0ddbeae55..199bf9ee9 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -325,7 +325,7 @@ func WithUser(user string) CtrCreateOption { // If useImageConfig is specified, image volumes, environment variables, and // other configuration from the image will be added to the config // TODO: Replace image name and ID with a libpod.Image struct when that is finished -func WithRootFSFromImage(imageID string, imageName string, useImageConfig bool) CtrCreateOption { +func WithRootFSFromImage(imageID string, imageName string, useImageVolumes bool) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return ErrCtrFinalized @@ -337,7 +337,7 @@ func WithRootFSFromImage(imageID string, imageName string, useImageConfig bool) ctr.config.RootfsImageID = imageID ctr.config.RootfsImageName = imageName - ctr.config.UseImageConfig = useImageConfig + ctr.config.ImageVolumes = useImageVolumes return nil } diff --git a/libpod/sql_state.go b/libpod/sql_state.go index 42fac831b..fe3232e62 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 = 6 +const DBSchema = 7 // SQLState is a state implementation backed by a persistent SQLite3 database type SQLState struct { @@ -104,7 +104,9 @@ func (s *SQLState) Refresh() (err error) { State=?, Mountpoint=?, Pid=?, - NetNSPath=?;` + NetNSPath=?, + IPAddress=?, + SubnetMask=?;` if !s.valid { return ErrDBClosed @@ -130,6 +132,8 @@ func (s *SQLState) Refresh() (err error) { ContainerStateConfigured, "", 0, + "", + "", "") if err != nil { return errors.Wrapf(err, "error refreshing database state") @@ -154,7 +158,9 @@ func (s *SQLState) Container(id string) (*Container, error) { containerState.ExitCode, containerState.OomKilled, containerState.Pid, - containerState.NetNSPath + containerState.NetNSPath, + containerState.IPAddress, + containerState.SubnetMask FROM containers INNER JOIN containerState ON containers.Id = containerState.Id @@ -170,7 +176,7 @@ func (s *SQLState) Container(id string) (*Container, error) { row := s.db.QueryRow(query, id) - ctr, err := ctrFromScannable(row, s.runtime, s.specsDir, s.lockDir) + ctr, err := s.ctrFromScannable(row) if err != nil { return nil, errors.Wrapf(err, "error retrieving container %s from database", id) } @@ -190,7 +196,9 @@ func (s *SQLState) LookupContainer(idOrName string) (*Container, error) { containerState.ExitCode, containerState.OomKilled, containerState.Pid, - containerState.NetNSPath + containerState.NetNSPath, + containerState.IPAddress, + containerState.SubnetMask FROM containers INNER JOIN containerState ON containers.Id = containerState.Id @@ -218,7 +226,7 @@ func (s *SQLState) LookupContainer(idOrName string) (*Container, error) { } var err error - ctr, err = ctrFromScannable(rows, s.runtime, s.specsDir, s.lockDir) + ctr, err = s.ctrFromScannable(rows) if err != nil { return nil, errors.Wrapf(err, "error retrieving container %s from database", idOrName) } @@ -271,10 +279,17 @@ func (s *SQLState) HasContainer(id string) (bool, error) { func (s *SQLState) AddContainer(ctr *Container) (err error) { const ( addCtr = `INSERT INTO containers VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ? );` addCtrState = `INSERT INTO containerState VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? + ?, ?, ?, ?, ?, + ?, ?, ?, ?, ?, + ?, ?, ? );` ) @@ -286,11 +301,6 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { return ErrCtrRemoved } - labelsJSON, err := json.Marshal(ctr.config.Labels) - if err != nil { - return errors.Wrapf(err, "error marshaling container %s labels to JSON", ctr.ID()) - } - mounts, err := json.Marshal(ctr.config.Mounts) if err != nil { return errors.Wrapf(err, "error marshaling container %s mounts to JSON", ctr.ID()) @@ -301,6 +311,11 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { return errors.Wrapf(err, "error marshaling container %s port mappings to JSON", ctr.ID()) } + labelsJSON, err := json.Marshal(ctr.config.Labels) + if err != nil { + return errors.Wrapf(err, "error marshaling container %s labels to JSON", ctr.ID()) + } + netNSPath := "" if ctr.state.NetNS != nil { netNSPath = ctr.state.NetNS.Path() @@ -322,22 +337,37 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { _, err = tx.Exec(addCtr, ctr.ID(), ctr.Name(), + stringToNullString(ctr.PodID()), + + ctr.config.RootfsImageID, + ctr.config.RootfsImageName, + boolToSQL(ctr.config.ImageVolumes), + boolToSQL(ctr.config.ReadOnly), + ctr.config.ShmDir, + ctr.config.ShmSize, + ctr.config.StaticDir, + string(mounts), + ctr.config.ProcessLabel, ctr.config.MountLabel, - string(mounts), - ctr.config.ShmDir, + ctr.config.User, + + stringToNullString(ctr.config.IPCNsCtr), + stringToNullString(ctr.config.MountNsCtr), + stringToNullString(ctr.config.NetNsCtr), + stringToNullString(ctr.config.PIDNsCtr), + stringToNullString(ctr.config.UserNsCtr), + stringToNullString(ctr.config.UTSNsCtr), + boolToSQL(ctr.config.CreateNetNS), string(portsJSON), - ctr.config.StaticDir, + boolToSQL(ctr.config.Stdin), string(labelsJSON), ctr.config.StopSignal, ctr.config.StopTimeout, timeToSQL(ctr.config.CreatedTime), - ctr.config.RootfsImageID, - ctr.config.RootfsImageName, - boolToSQL(ctr.config.UseImageConfig), - ctr.config.User) + ctr.config.CgroupParent) if err != nil { return errors.Wrapf(err, "error adding static information for container %s to database", ctr.ID()) } @@ -354,7 +384,9 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) { ctr.state.ExitCode, boolToSQL(ctr.state.OOMKilled), ctr.state.PID, - netNSPath) + netNSPath, + ctr.state.IPAddress, + ctr.state.SubnetMask) if err != nil { return errors.Wrapf(err, "error adding container %s state to database", ctr.ID()) } @@ -394,7 +426,9 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { ExitCode, OomKilled, Pid, - NetNSPath + NetNSPath, + IPAddress, + SubnetMask FROM containerState WHERE ID=?;` var ( @@ -408,6 +442,8 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { oomKilled int pid int netNSPath string + ipAddress string + subnetMask string ) if !s.valid { @@ -429,7 +465,9 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { &exitCode, &oomKilled, &pid, - &netNSPath) + &netNSPath, + &ipAddress, + &subnetMask) if err != nil { // The container may not exist in the database if err == sql.ErrNoRows { @@ -451,6 +489,8 @@ func (s *SQLState) UpdateContainer(ctr *Container) error { newState.ExitCode = exitCode newState.OOMKilled = boolFromSQL(oomKilled) newState.PID = pid + newState.IPAddress = ipAddress + newState.SubnetMask = subnetMask if newState.Mountpoint != "" { newState.Mounted = true @@ -512,7 +552,9 @@ func (s *SQLState) SaveContainer(ctr *Container) error { ExitCode=?, OomKilled=?, Pid=?, - NetNSPath=? + NetNSPath=?, + IPAddress=?, + SubnetMask=? WHERE Id=?;` if !ctr.valid { @@ -552,6 +594,8 @@ func (s *SQLState) SaveContainer(ctr *Container) error { boolToSQL(ctr.state.OOMKilled), ctr.state.PID, netNSPath, + ctr.state.IPAddress, + ctr.state.SubnetMask, ctr.ID()) if err != nil { return errors.Wrapf(err, "error updating container %s state in database", ctr.ID()) @@ -642,7 +686,9 @@ func (s *SQLState) AllContainers() ([]*Container, error) { containerState.ExitCode, containerState.OomKilled, containerState.Pid, - containerState.NetNSPath + containerState.NetNSPath, + containerState.IPAddress, + containerState.SubnetMask FROM containers INNER JOIN containerState ON containers.Id = containerState.Id @@ -661,7 +707,7 @@ func (s *SQLState) AllContainers() ([]*Container, error) { containers := []*Container{} for rows.Next() { - ctr, err := ctrFromScannable(rows, s.runtime, s.specsDir, s.lockDir) + ctr, err := s.ctrFromScannable(rows) if err != nil { return nil, err } diff --git a/libpod/sql_state_internal.go b/libpod/sql_state_internal.go index 5904f4254..ef3b6bd4e 100644 --- a/libpod/sql_state_internal.go +++ b/libpod/sql_state_internal.go @@ -22,13 +22,13 @@ func checkDB(db *sql.DB, r *Runtime) (err error) { // TODO: Include UID/GID mappings const runtimeTable = ` CREATE TABLE runtime( - Id INTEGER NOT NULL PRIMARY KEY, - SchemaVersion INTEGER NOT NULL, - StaticDir TEXT NOT NULL, - TmpDir TEXT NOT NULL, - RunRoot TEXT NOT NULL, - GraphRoot TEXT NOT NULL, - GraphDriverName TEXT NOT NULL, + Id INTEGER NOT NULL PRIMARY KEY, + SchemaVersion INTEGER NOT NULL, + StaticDir TEXT NOT NULL, + TmpDir TEXT NOT NULL, + RunRoot TEXT NOT NULL, + GraphRoot TEXT NOT NULL, + GraphDriverName TEXT NOT NULL, CHECK (Id=0) ); ` @@ -155,6 +155,7 @@ func prepareDB(db *sql.DB) (err error) { // TODO add ctr shared namespaces information - A separate table, probably? So we can FOREIGN KEY the ID // TODO schema migration might be necessary and should be handled here // TODO maybe make a port mappings table instead of JSONing the array and storing it? + // TODO prepared statements for common queries for performance // Enable foreign keys in SQLite if _, err := db.Exec("PRAGMA foreign_keys = ON;"); err != nil { @@ -164,51 +165,88 @@ func prepareDB(db *sql.DB) (err error) { // Create a table for unchanging container data const createCtr = ` CREATE TABLE IF NOT EXISTS containers( - Id TEXT NOT NULL PRIMARY KEY, - Name TEXT NOT NULL UNIQUE, - ProcessLabel TEXT NOT NULL, - MountLabel TEXT NOT NULL, - Mounts TEXT NOT NULL, - ShmDir TEXT NOT NULL, - CreateNetNS INTEGER NOT NULL, - PortMappings TEXT NOT NULL, - StaticDir TEXT NOT NULL, - Stdin INTEGER NOT NULL, - LabelsJSON TEXT NOT NULL, - StopSignal INTEGER NOT NULL, - StopTimeout INTEGER NOT NULL, - CreatedTime TEXT NOT NULL, - RootfsImageID TEXT NOT NULL, - RootfsImageName TEXT NOT NULL, - UseImageConfig INTEGER NOT NULL, - User TEXT NOT NULL, - CHECK (Stdin IN (0, 1)), + Id TEXT NOT NULL PRIMARY KEY, + Name TEXT NOT NULL UNIQUE, + Pod TEXT, + + RootfsImageID TEXT NOT NULL, + RootfsImageName TEXT NOT NULL, + ImageVolumes INTEGER NOT NULL, + ReadOnly INTEGER NOT NULL, + ShmDir TEXT NOT NULL, + ShmSize INTEGER NOT NULL, + StaticDir TEXT NOT NULL, + Mounts TEXT NOT NULL, + + ProcessLabel TEXT NOT NULL, + MountLabel TEXT NOT NULL, + User TEXT NOT NULL, + + IPCNsCtr TEXT, + MountNsCtr TEXT, + NetNsCtr TEXT, + PIDNsCtr TEXT, + UserNsCtr TEXT, + UTSNsCtr TEXT, + + CreateNetNS INTEGER NOT NULL, + PortMappings TEXT NOT NULL, + + Stdin INTEGER NOT NULL, + LabelsJSON TEXT NOT NULL, + StopSignal INTEGER NOT NULL, + StopTimeout INTEGER NOT NULL, + CreatedTime TEXT NOT NULL, + CgroupParent TEXT NOT NULL, + + CHECK (ImageVolumes IN (0, 1)), + CHECK (ReadOnly IN (0, 1)), + CHECK (SHMSize>=0), CHECK (CreateNetNS IN (0, 1)), - CHECK (UseImageConfig IN (0, 1)), - CHECK (StopSignal>=0) + CHECK (Stdin IN (0, 1)), + CHECK (StopSignal>=0), + FOREIGN KEY (Pod) REFERENCES pod(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (IPCNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (MountNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (NetNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (PIDNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (UserNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED, + FOREIGN KEY (UTSNsCtr) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED ); ` // Create a table for changing container state const createCtrState = ` CREATE TABLE IF NOT EXISTS containerState( - Id TEXT NOT NULL PRIMARY KEY, - State INTEGER NOT NULL, - ConfigPath TEXT NOT NULL, - RunDir TEXT NOT NULL, - Mountpoint TEXT NOT NULL, - StartedTime TEXT NUT NULL, - FinishedTime TEXT NOT NULL, - ExitCode INTEGER NOT NULL, - OomKilled INTEGER NOT NULL, - Pid INTEGER NOT NULL, - NetNSPath TEXT NOT NULL, + Id TEXT NOT NULL PRIMARY KEY, + State INTEGER NOT NULL, + ConfigPath TEXT NOT NULL, + RunDir TEXT NOT NULL, + Mountpoint TEXT NOT NULL, + StartedTime TEXT NUT NULL, + FinishedTime TEXT NOT NULL, + ExitCode INTEGER NOT NULL, + OomKilled INTEGER NOT NULL, + Pid INTEGER NOT NULL, + NetNSPath TEXT NOT NULL, + IPAddress TEXT NOT NULL, + SubnetMask TEXT NOT NULL, + CHECK (State>0), CHECK (OomKilled IN (0, 1)), FOREIGN KEY (Id) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED ); ` + // Create a table for pod config + const createPod = ` + CREATE TABLE IF NOT EXISTS pod( + Id TEXT NOT NULL PRIMARY KEY, + Name TEXT NOT NULL UNIQUE, + Labels TEXT NOT NULL + ); + ` + // Create the tables tx, err := db.Begin() if err != nil { @@ -229,6 +267,9 @@ func prepareDB(db *sql.DB) (err error) { if _, err := tx.Exec(createCtrState); err != nil { return errors.Wrapf(err, "error creating container state table in database") } + if _, err := tx.Exec(createPod); err != nil { + return errors.Wrapf(err, "error creating pods table in database") + } if err := tx.Commit(); err != nil { return errors.Wrapf(err, "error committing table creation transaction in database") @@ -251,6 +292,25 @@ func boolToSQL(b bool) int { return 0 } +// Convert a null string from SQL-readable format +func stringFromNullString(s sql.NullString) string { + if s.Valid { + return s.String + } + return "" +} + +// Convert a string to a SQL nullable string +func stringToNullString(s string) sql.NullString { + if s == "" { + return sql.NullString{} + } + return sql.NullString{ + String: s, + Valid: true, + } +} + // Convert a bool from SQL-readable format func boolFromSQL(i int) bool { return i != 0 @@ -272,26 +332,42 @@ type scannable interface { } // Read a single container from a single row result in the database -func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir string) (*Container, error) { +func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) { var ( - id string - name string - processLabel string - mountLabel string - mounts string - shmDir string - createNetNS int - portMappingsJSON string - staticDir string - stdin int - labelsJSON string - stopSignal uint - stopTimeout uint - createdTimeString string - rootfsImageID string - rootfsImageName string - useImageConfig int - user string + id string + name string + pod sql.NullString + + rootfsImageID string + rootfsImageName string + imageVolumes int + readOnly int + shmDir string + shmSize int64 + staticDir string + mounts string + + processLabel string + mountLabel string + user string + + ipcNsCtrNullStr sql.NullString + mountNsCtrNullStr sql.NullString + netNsCtrNullStr sql.NullString + pidNsCtrNullStr sql.NullString + userNsCtrNullStr sql.NullString + utsNsCtrNullStr sql.NullString + + createNetNS int + portMappingsJSON string + + stdin int + labelsJSON string + stopSignal uint + stopTimeout uint + createdTimeString string + cgroupParent string + state int configPath string runDir string @@ -302,27 +378,45 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir oomKilled int pid int netNSPath string + ipAddress string + subnetMask string ) err := row.Scan( &id, &name, + &pod, + + &rootfsImageID, + &rootfsImageName, + &imageVolumes, + &readOnly, + &shmDir, + &shmSize, + &staticDir, + &mounts, + &processLabel, &mountLabel, - &mounts, - &shmDir, + &user, + + &ipcNsCtrNullStr, + &mountNsCtrNullStr, + &netNsCtrNullStr, + &pidNsCtrNullStr, + &userNsCtrNullStr, + &utsNsCtrNullStr, + &createNetNS, &portMappingsJSON, - &staticDir, + &stdin, &labelsJSON, &stopSignal, &stopTimeout, &createdTimeString, - &rootfsImageID, - &rootfsImageName, - &useImageConfig, - &user, + &cgroupParent, + &state, &configPath, &runDir, @@ -332,7 +426,9 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir &exitCode, &oomKilled, &pid, - &netNSPath) + &netNSPath, + &ipAddress, + &subnetMask) if err != nil { if err == sql.ErrNoRows { return nil, ErrNoSuchCtr @@ -347,18 +443,33 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir ctr.config.ID = id ctr.config.Name = name + ctr.config.Pod = stringFromNullString(pod) + ctr.config.RootfsImageID = rootfsImageID ctr.config.RootfsImageName = rootfsImageName - ctr.config.UseImageConfig = boolFromSQL(useImageConfig) + ctr.config.ImageVolumes = boolFromSQL(imageVolumes) + ctr.config.ReadOnly = boolFromSQL(readOnly) + ctr.config.ShmDir = shmDir + ctr.config.ShmSize = shmSize + ctr.config.StaticDir = staticDir + ctr.config.ProcessLabel = processLabel ctr.config.MountLabel = mountLabel - ctr.config.ShmDir = shmDir + ctr.config.User = user + + ctr.config.IPCNsCtr = stringFromNullString(ipcNsCtrNullStr) + ctr.config.MountNsCtr = stringFromNullString(mountNsCtrNullStr) + ctr.config.NetNsCtr = stringFromNullString(netNsCtrNullStr) + ctr.config.PIDNsCtr = stringFromNullString(pidNsCtrNullStr) + ctr.config.UserNsCtr = stringFromNullString(userNsCtrNullStr) + ctr.config.UTSNsCtr = stringFromNullString(utsNsCtrNullStr) + ctr.config.CreateNetNS = boolFromSQL(createNetNS) - ctr.config.StaticDir = staticDir + ctr.config.Stdin = boolFromSQL(stdin) ctr.config.StopSignal = stopSignal ctr.config.StopTimeout = stopTimeout - ctr.config.User = user + ctr.config.CgroupParent = cgroupParent ctr.state.State = ContainerState(state) ctr.state.ConfigPath = configPath @@ -367,18 +478,14 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir ctr.state.ExitCode = exitCode ctr.state.OOMKilled = boolFromSQL(oomKilled) ctr.state.PID = pid + ctr.state.IPAddress = ipAddress + ctr.state.SubnetMask = subnetMask // TODO should we store this in the database separately instead? if ctr.state.Mountpoint != "" { ctr.state.Mounted = true } - labels := make(map[string]string) - if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { - return nil, errors.Wrapf(err, "error parsing container %s labels JSON", id) - } - ctr.config.Labels = labels - if err := json.Unmarshal([]byte(mounts), &ctr.config.Mounts); err != nil { return nil, errors.Wrapf(err, "error parsing container %s mounts JSON", id) } @@ -387,6 +494,12 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir return nil, errors.Wrapf(err, "error parsing container %s port mappings JSON", id) } + labels := make(map[string]string) + if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { + return nil, errors.Wrapf(err, "error parsing container %s labels JSON", id) + } + ctr.config.Labels = labels + createdTime, err := timeFromSQL(createdTimeString) if err != nil { return nil, errors.Wrapf(err, "error parsing container %s created time", id) @@ -415,10 +528,10 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir } ctr.valid = true - ctr.runtime = runtime + ctr.runtime = s.runtime // Open and set the lockfile - lockPath := filepath.Join(lockDir, id) + lockPath := filepath.Join(s.lockDir, id) lock, err := storage.GetLockfile(lockPath) if err != nil { return nil, errors.Wrapf(err, "error retrieving lockfile for container %s", id) @@ -427,7 +540,7 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir // Retrieve the spec from disk ociSpec := new(spec.Spec) - specPath := getSpecPath(specsDir, id) + specPath := getSpecPath(s.specsDir, id) fileContents, err := ioutil.ReadFile(specPath) if err != nil { return nil, errors.Wrapf(err, "error reading container %s OCI spec", id) diff --git a/libpod/sql_state_test.go b/libpod/sql_state_test.go index e990df6c9..020e2ce40 100644 --- a/libpod/sql_state_test.go +++ b/libpod/sql_state_test.go @@ -21,7 +21,8 @@ func getTestContainer(id, name, locksDir string) (*Container, error) { Name: name, RootfsImageID: id, RootfsImageName: "testimg", - UseImageConfig: true, + ImageVolumes: true, + ReadOnly: true, StaticDir: "/does/not/exist/", Stdin: true, Labels: make(map[string]string), -- cgit v1.2.3-54-g00ecf