Update DB to add new fields required for planned features

Signed-off-by: Matthew Heon <matthew.heon@gmail.com>

Closes: #209
Approved by: rhatdan
This commit is contained in:
Matthew Heon
2018-01-10 13:49:38 -05:00
committed by Atomic Bot
parent f0843e94cf
commit e6be800ec6
6 changed files with 338 additions and 132 deletions

View File

@ -61,8 +61,8 @@ const (
artifactsDir = "artifacts" artifactsDir = "artifacts"
) )
// CGroupParent is the prefix to a cgroup path in libpod // CgroupParent is the default prefix to a cgroup path in libpod
var CGroupParent = "/libpod_parent" var CgroupParent = "/libpod_parent"
// Container is a single OCI container // Container is a single OCI container
type Container struct { type Container struct {
@ -78,6 +78,13 @@ type Container struct {
runtime *Runtime 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 // containerRuntimeInfo contains the current state of the container
// It is stored on disk in a tmpfs and recreated on reboot // It is stored on disk in a tmpfs and recreated on reboot
type containerRuntimeInfo struct { type containerRuntimeInfo struct {
@ -107,7 +114,10 @@ type containerRuntimeInfo struct {
// Will only be set if config.CreateNetNS is true, or the container was // Will only be set if config.CreateNetNS is true, or the container was
// told to join another container's network namespace // told to join another container's network namespace
NetNS ns.NetNS 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 // ContainerConfig contains all information that was used to create the
@ -117,49 +127,76 @@ type ContainerConfig struct {
Spec *spec.Spec `json:"spec"` Spec *spec.Spec `json:"spec"`
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` 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 // Information on the image used for the root filesystem
RootfsImageID string `json:"rootfsImageID,omitempty"` RootfsImageID string `json:"rootfsImageID,omitempty"`
RootfsImageName string `json:"rootfsImageName,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 // SELinux process label for container
ProcessLabel string `json:"ProcessLabel,omitempty"` ProcessLabel string `json:"ProcessLabel,omitempty"`
// SELinux mount label for root filesystem // SELinux mount label for root filesystem
MountLabel string `json:"MountLabel,omitempty"` MountLabel string `json:"MountLabel,omitempty"`
// Src path to be mounted on /dev/shm in container // User and group to use in the container
ShmDir string `json:"ShmDir,omitempty"` // 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 // CreateNetNS indicates that libpod should create and configure a new
// network namespace for the container // network namespace for the container
// This cannot be set if NetNsCtr is also set
CreateNetNS bool `json:"createNetNS"` CreateNetNS bool `json:"createNetNS"`
// PortMappings are the ports forwarded to the container's network // PortMappings are the ports forwarded to the container's network
// namespace // namespace
// These are not used unless CreateNetNS is true // These are not used unless CreateNetNS is true
PortMappings []ocicni.PortMapping PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"`
// Static directory for container content that will persist across
// reboot // Misc Options
StaticDir string `json:"staticDir"`
// Whether to keep container STDIN open // Whether to keep container STDIN open
Stdin bool `json:"stdin,omitempty"` 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 // Labels is a set of key-value pairs providing additional information
// about a container // about a container
Labels map[string]string `json:"labels,omitempty"` 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 is the signal that will be used to stop the container
StopSignal uint `json:"stopSignal,omitempty"` StopSignal uint `json:"stopSignal,omitempty"`
// StopTimeout is the signal that will be used to stop the container // StopTimeout is the signal that will be used to stop the container
StopTimeout uint `json:"stopTimeout,omitempty"` StopTimeout uint `json:"stopTimeout,omitempty"`
// Shared namespaces with container
SharedNamespaceCtr *string `json:"shareNamespacesWith,omitempty"`
SharedNamespaceMap map[string]string `json:"sharedNamespaces"`
// Time container was created // Time container was created
CreatedTime time.Time `json:"createdTime"` CreatedTime time.Time `json:"createdTime"`
// User/GID to use within the container // Cgroup parent of the container
User string `json:"user"` CgroupParent string `json:"cgroupParent"`
// TODO save log location here and pass into OCI code // TODO log options - logpath for plaintext, others for log drivers
// TODO allow overriding of log path
} }
// ContainerStater returns a string representation for users // ContainerStater returns a string representation for users
@ -192,6 +229,12 @@ func (c *Container) Name() string {
return c.config.Name 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 // ShmDir returns the sources path to be mounted on /dev/shm in container
func (c *Container) ShmDir() string { func (c *Container) ShmDir() string {
return c.config.ShmDir return c.config.ShmDir
@ -468,6 +511,9 @@ func newContainer(rspec *spec.Spec, lockDir string) (*Container, error) {
deepcopier.Copy(rspec).To(ctr.config.Spec) deepcopier.Copy(rspec).To(ctr.config.Spec)
ctr.config.CreatedTime = time.Now() ctr.config.CreatedTime = time.Now()
ctr.config.ShmSize = DefaultShmSize
ctr.config.CgroupParent = CgroupParent
// Path our lock file will reside at // Path our lock file will reside at
lockPath := filepath.Join(lockDir, ctr.config.ID) lockPath := filepath.Join(lockDir, ctr.config.ID)
// Grab a lockfile at the given path // 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 // With the spec complete, do an OCI create
// TODO set cgroup parent in a sane fashion // 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 return err
} }
@ -1182,7 +1228,7 @@ func (c *Container) cleanupStorage() error {
// CGroupPath returns a cgroups "path" for a given container. // CGroupPath returns a cgroups "path" for a given container.
func (c *Container) CGroupPath() cgroups.Path { 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 // copyHostFileToRundir copies the provided file to the runtimedir

View File

@ -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 // Gets the pids for a container without locking. should only be called from a func where
// locking has already been established. // locking has already been established.
func (c *Container) getContainerPids() ([]string, error) { 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) logrus.Debug("reading pids from ", taskFile)
content, err := ioutil.ReadFile(taskFile) content, err := ioutil.ReadFile(taskFile)
if err != nil { if err != nil {

View File

@ -325,7 +325,7 @@ func WithUser(user string) CtrCreateOption {
// If useImageConfig is specified, image volumes, environment variables, and // If useImageConfig is specified, image volumes, environment variables, and
// other configuration from the image will be added to the config // 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 // 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 { return func(ctr *Container) error {
if ctr.valid { if ctr.valid {
return ErrCtrFinalized return ErrCtrFinalized
@ -337,7 +337,7 @@ func WithRootFSFromImage(imageID string, imageName string, useImageConfig bool)
ctr.config.RootfsImageID = imageID ctr.config.RootfsImageID = imageID
ctr.config.RootfsImageName = imageName ctr.config.RootfsImageName = imageName
ctr.config.UseImageConfig = useImageConfig ctr.config.ImageVolumes = useImageVolumes
return nil return nil
} }

View File

@ -15,7 +15,7 @@ import (
// DBSchema is the current DB schema version // DBSchema is the current DB schema version
// Increments every time a change is made to the database's tables // 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 // SQLState is a state implementation backed by a persistent SQLite3 database
type SQLState struct { type SQLState struct {
@ -104,7 +104,9 @@ func (s *SQLState) Refresh() (err error) {
State=?, State=?,
Mountpoint=?, Mountpoint=?,
Pid=?, Pid=?,
NetNSPath=?;` NetNSPath=?,
IPAddress=?,
SubnetMask=?;`
if !s.valid { if !s.valid {
return ErrDBClosed return ErrDBClosed
@ -130,6 +132,8 @@ func (s *SQLState) Refresh() (err error) {
ContainerStateConfigured, ContainerStateConfigured,
"", "",
0, 0,
"",
"",
"") "")
if err != nil { if err != nil {
return errors.Wrapf(err, "error refreshing database state") return errors.Wrapf(err, "error refreshing database state")
@ -154,7 +158,9 @@ func (s *SQLState) Container(id string) (*Container, error) {
containerState.ExitCode, containerState.ExitCode,
containerState.OomKilled, containerState.OomKilled,
containerState.Pid, containerState.Pid,
containerState.NetNSPath containerState.NetNSPath,
containerState.IPAddress,
containerState.SubnetMask
FROM containers FROM containers
INNER JOIN INNER JOIN
containerState ON containers.Id = containerState.Id containerState ON containers.Id = containerState.Id
@ -170,7 +176,7 @@ func (s *SQLState) Container(id string) (*Container, error) {
row := s.db.QueryRow(query, id) row := s.db.QueryRow(query, id)
ctr, err := ctrFromScannable(row, s.runtime, s.specsDir, s.lockDir) ctr, err := s.ctrFromScannable(row)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error retrieving container %s from database", id) 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.ExitCode,
containerState.OomKilled, containerState.OomKilled,
containerState.Pid, containerState.Pid,
containerState.NetNSPath containerState.NetNSPath,
containerState.IPAddress,
containerState.SubnetMask
FROM containers FROM containers
INNER JOIN INNER JOIN
containerState ON containers.Id = containerState.Id containerState ON containers.Id = containerState.Id
@ -218,7 +226,7 @@ func (s *SQLState) LookupContainer(idOrName string) (*Container, error) {
} }
var err error var err error
ctr, err = ctrFromScannable(rows, s.runtime, s.specsDir, s.lockDir) ctr, err = s.ctrFromScannable(rows)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error retrieving container %s from database", idOrName) 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) { func (s *SQLState) AddContainer(ctr *Container) (err error) {
const ( const (
addCtr = `INSERT INTO containers VALUES ( addCtr = `INSERT INTO containers VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?
);` );`
addCtrState = `INSERT INTO containerState VALUES ( addCtrState = `INSERT INTO containerState VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?
);` );`
) )
@ -286,11 +301,6 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) {
return ErrCtrRemoved 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) mounts, err := json.Marshal(ctr.config.Mounts)
if err != nil { if err != nil {
return errors.Wrapf(err, "error marshaling container %s mounts to JSON", ctr.ID()) 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()) 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 := "" netNSPath := ""
if ctr.state.NetNS != nil { if ctr.state.NetNS != nil {
netNSPath = ctr.state.NetNS.Path() netNSPath = ctr.state.NetNS.Path()
@ -322,22 +337,37 @@ func (s *SQLState) AddContainer(ctr *Container) (err error) {
_, err = tx.Exec(addCtr, _, err = tx.Exec(addCtr,
ctr.ID(), ctr.ID(),
ctr.Name(), 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.ProcessLabel,
ctr.config.MountLabel, ctr.config.MountLabel,
string(mounts), ctr.config.User,
ctr.config.ShmDir,
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), boolToSQL(ctr.config.CreateNetNS),
string(portsJSON), string(portsJSON),
ctr.config.StaticDir,
boolToSQL(ctr.config.Stdin), boolToSQL(ctr.config.Stdin),
string(labelsJSON), string(labelsJSON),
ctr.config.StopSignal, ctr.config.StopSignal,
ctr.config.StopTimeout, ctr.config.StopTimeout,
timeToSQL(ctr.config.CreatedTime), timeToSQL(ctr.config.CreatedTime),
ctr.config.RootfsImageID, ctr.config.CgroupParent)
ctr.config.RootfsImageName,
boolToSQL(ctr.config.UseImageConfig),
ctr.config.User)
if err != nil { if err != nil {
return errors.Wrapf(err, "error adding static information for container %s to database", ctr.ID()) 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, ctr.state.ExitCode,
boolToSQL(ctr.state.OOMKilled), boolToSQL(ctr.state.OOMKilled),
ctr.state.PID, ctr.state.PID,
netNSPath) netNSPath,
ctr.state.IPAddress,
ctr.state.SubnetMask)
if err != nil { if err != nil {
return errors.Wrapf(err, "error adding container %s state to database", ctr.ID()) 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, ExitCode,
OomKilled, OomKilled,
Pid, Pid,
NetNSPath NetNSPath,
IPAddress,
SubnetMask
FROM containerState WHERE ID=?;` FROM containerState WHERE ID=?;`
var ( var (
@ -408,6 +442,8 @@ func (s *SQLState) UpdateContainer(ctr *Container) error {
oomKilled int oomKilled int
pid int pid int
netNSPath string netNSPath string
ipAddress string
subnetMask string
) )
if !s.valid { if !s.valid {
@ -429,7 +465,9 @@ func (s *SQLState) UpdateContainer(ctr *Container) error {
&exitCode, &exitCode,
&oomKilled, &oomKilled,
&pid, &pid,
&netNSPath) &netNSPath,
&ipAddress,
&subnetMask)
if err != nil { if err != nil {
// The container may not exist in the database // The container may not exist in the database
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@ -451,6 +489,8 @@ func (s *SQLState) UpdateContainer(ctr *Container) error {
newState.ExitCode = exitCode newState.ExitCode = exitCode
newState.OOMKilled = boolFromSQL(oomKilled) newState.OOMKilled = boolFromSQL(oomKilled)
newState.PID = pid newState.PID = pid
newState.IPAddress = ipAddress
newState.SubnetMask = subnetMask
if newState.Mountpoint != "" { if newState.Mountpoint != "" {
newState.Mounted = true newState.Mounted = true
@ -512,7 +552,9 @@ func (s *SQLState) SaveContainer(ctr *Container) error {
ExitCode=?, ExitCode=?,
OomKilled=?, OomKilled=?,
Pid=?, Pid=?,
NetNSPath=? NetNSPath=?,
IPAddress=?,
SubnetMask=?
WHERE Id=?;` WHERE Id=?;`
if !ctr.valid { if !ctr.valid {
@ -552,6 +594,8 @@ func (s *SQLState) SaveContainer(ctr *Container) error {
boolToSQL(ctr.state.OOMKilled), boolToSQL(ctr.state.OOMKilled),
ctr.state.PID, ctr.state.PID,
netNSPath, netNSPath,
ctr.state.IPAddress,
ctr.state.SubnetMask,
ctr.ID()) ctr.ID())
if err != nil { if err != nil {
return errors.Wrapf(err, "error updating container %s state in database", ctr.ID()) 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.ExitCode,
containerState.OomKilled, containerState.OomKilled,
containerState.Pid, containerState.Pid,
containerState.NetNSPath containerState.NetNSPath,
containerState.IPAddress,
containerState.SubnetMask
FROM containers FROM containers
INNER JOIN INNER JOIN
containerState ON containers.Id = containerState.Id containerState ON containers.Id = containerState.Id
@ -661,7 +707,7 @@ func (s *SQLState) AllContainers() ([]*Container, error) {
containers := []*Container{} containers := []*Container{}
for rows.Next() { for rows.Next() {
ctr, err := ctrFromScannable(rows, s.runtime, s.specsDir, s.lockDir) ctr, err := s.ctrFromScannable(rows)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -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 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 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 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 // Enable foreign keys in SQLite
if _, err := db.Exec("PRAGMA foreign_keys = ON;"); err != nil { if _, err := db.Exec("PRAGMA foreign_keys = ON;"); err != nil {
@ -166,26 +167,51 @@ func prepareDB(db *sql.DB) (err error) {
CREATE TABLE IF NOT EXISTS containers( CREATE TABLE IF NOT EXISTS containers(
Id TEXT NOT NULL PRIMARY KEY, Id TEXT NOT NULL PRIMARY KEY,
Name TEXT NOT NULL UNIQUE, 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, ProcessLabel TEXT NOT NULL,
MountLabel TEXT NOT NULL, MountLabel TEXT NOT NULL,
Mounts TEXT NOT NULL, User TEXT NOT NULL,
ShmDir TEXT NOT NULL,
IPCNsCtr TEXT,
MountNsCtr TEXT,
NetNsCtr TEXT,
PIDNsCtr TEXT,
UserNsCtr TEXT,
UTSNsCtr TEXT,
CreateNetNS INTEGER NOT NULL, CreateNetNS INTEGER NOT NULL,
PortMappings TEXT NOT NULL, PortMappings TEXT NOT NULL,
StaticDir TEXT NOT NULL,
Stdin INTEGER NOT NULL, Stdin INTEGER NOT NULL,
LabelsJSON TEXT NOT NULL, LabelsJSON TEXT NOT NULL,
StopSignal INTEGER NOT NULL, StopSignal INTEGER NOT NULL,
StopTimeout INTEGER NOT NULL, StopTimeout INTEGER NOT NULL,
CreatedTime TEXT NOT NULL, CreatedTime TEXT NOT NULL,
RootfsImageID TEXT NOT NULL, CgroupParent TEXT NOT NULL,
RootfsImageName TEXT NOT NULL,
UseImageConfig INTEGER NOT NULL, CHECK (ImageVolumes IN (0, 1)),
User TEXT NOT NULL, CHECK (ReadOnly IN (0, 1)),
CHECK (Stdin IN (0, 1)), CHECK (SHMSize>=0),
CHECK (CreateNetNS IN (0, 1)), CHECK (CreateNetNS IN (0, 1)),
CHECK (UseImageConfig IN (0, 1)), CHECK (Stdin IN (0, 1)),
CHECK (StopSignal>=0) 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
); );
` `
@ -203,12 +229,24 @@ func prepareDB(db *sql.DB) (err error) {
OomKilled INTEGER NOT NULL, OomKilled INTEGER NOT NULL,
Pid INTEGER NOT NULL, Pid INTEGER NOT NULL,
NetNSPath TEXT NOT NULL, NetNSPath TEXT NOT NULL,
IPAddress TEXT NOT NULL,
SubnetMask TEXT NOT NULL,
CHECK (State>0), CHECK (State>0),
CHECK (OomKilled IN (0, 1)), CHECK (OomKilled IN (0, 1)),
FOREIGN KEY (Id) REFERENCES containers(Id) DEFERRABLE INITIALLY DEFERRED 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 // Create the tables
tx, err := db.Begin() tx, err := db.Begin()
if err != nil { if err != nil {
@ -229,6 +267,9 @@ func prepareDB(db *sql.DB) (err error) {
if _, err := tx.Exec(createCtrState); err != nil { if _, err := tx.Exec(createCtrState); err != nil {
return errors.Wrapf(err, "error creating container state table in database") 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 { if err := tx.Commit(); err != nil {
return errors.Wrapf(err, "error committing table creation transaction in database") return errors.Wrapf(err, "error committing table creation transaction in database")
@ -251,6 +292,25 @@ func boolToSQL(b bool) int {
return 0 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 // Convert a bool from SQL-readable format
func boolFromSQL(i int) bool { func boolFromSQL(i int) bool {
return i != 0 return i != 0
@ -272,26 +332,42 @@ type scannable interface {
} }
// Read a single container from a single row result in the database // 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 ( var (
id string id string
name string name string
pod sql.NullString
rootfsImageID string
rootfsImageName string
imageVolumes int
readOnly int
shmDir string
shmSize int64
staticDir string
mounts string
processLabel string processLabel string
mountLabel string mountLabel string
mounts string user string
shmDir string
ipcNsCtrNullStr sql.NullString
mountNsCtrNullStr sql.NullString
netNsCtrNullStr sql.NullString
pidNsCtrNullStr sql.NullString
userNsCtrNullStr sql.NullString
utsNsCtrNullStr sql.NullString
createNetNS int createNetNS int
portMappingsJSON string portMappingsJSON string
staticDir string
stdin int stdin int
labelsJSON string labelsJSON string
stopSignal uint stopSignal uint
stopTimeout uint stopTimeout uint
createdTimeString string createdTimeString string
rootfsImageID string cgroupParent string
rootfsImageName string
useImageConfig int
user string
state int state int
configPath string configPath string
runDir string runDir string
@ -302,27 +378,45 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir
oomKilled int oomKilled int
pid int pid int
netNSPath string netNSPath string
ipAddress string
subnetMask string
) )
err := row.Scan( err := row.Scan(
&id, &id,
&name, &name,
&pod,
&rootfsImageID,
&rootfsImageName,
&imageVolumes,
&readOnly,
&shmDir,
&shmSize,
&staticDir,
&mounts,
&processLabel, &processLabel,
&mountLabel, &mountLabel,
&mounts, &user,
&shmDir,
&ipcNsCtrNullStr,
&mountNsCtrNullStr,
&netNsCtrNullStr,
&pidNsCtrNullStr,
&userNsCtrNullStr,
&utsNsCtrNullStr,
&createNetNS, &createNetNS,
&portMappingsJSON, &portMappingsJSON,
&staticDir,
&stdin, &stdin,
&labelsJSON, &labelsJSON,
&stopSignal, &stopSignal,
&stopTimeout, &stopTimeout,
&createdTimeString, &createdTimeString,
&rootfsImageID, &cgroupParent,
&rootfsImageName,
&useImageConfig,
&user,
&state, &state,
&configPath, &configPath,
&runDir, &runDir,
@ -332,7 +426,9 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir
&exitCode, &exitCode,
&oomKilled, &oomKilled,
&pid, &pid,
&netNSPath) &netNSPath,
&ipAddress,
&subnetMask)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, ErrNoSuchCtr return nil, ErrNoSuchCtr
@ -347,18 +443,33 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir
ctr.config.ID = id ctr.config.ID = id
ctr.config.Name = name ctr.config.Name = name
ctr.config.Pod = stringFromNullString(pod)
ctr.config.RootfsImageID = rootfsImageID ctr.config.RootfsImageID = rootfsImageID
ctr.config.RootfsImageName = rootfsImageName 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.ProcessLabel = processLabel
ctr.config.MountLabel = mountLabel 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.CreateNetNS = boolFromSQL(createNetNS)
ctr.config.StaticDir = staticDir
ctr.config.Stdin = boolFromSQL(stdin) ctr.config.Stdin = boolFromSQL(stdin)
ctr.config.StopSignal = stopSignal ctr.config.StopSignal = stopSignal
ctr.config.StopTimeout = stopTimeout ctr.config.StopTimeout = stopTimeout
ctr.config.User = user ctr.config.CgroupParent = cgroupParent
ctr.state.State = ContainerState(state) ctr.state.State = ContainerState(state)
ctr.state.ConfigPath = configPath ctr.state.ConfigPath = configPath
@ -367,18 +478,14 @@ func ctrFromScannable(row scannable, runtime *Runtime, specsDir string, lockDir
ctr.state.ExitCode = exitCode ctr.state.ExitCode = exitCode
ctr.state.OOMKilled = boolFromSQL(oomKilled) ctr.state.OOMKilled = boolFromSQL(oomKilled)
ctr.state.PID = pid ctr.state.PID = pid
ctr.state.IPAddress = ipAddress
ctr.state.SubnetMask = subnetMask
// TODO should we store this in the database separately instead? // TODO should we store this in the database separately instead?
if ctr.state.Mountpoint != "" { if ctr.state.Mountpoint != "" {
ctr.state.Mounted = true 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 { if err := json.Unmarshal([]byte(mounts), &ctr.config.Mounts); err != nil {
return nil, errors.Wrapf(err, "error parsing container %s mounts JSON", id) 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) 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) createdTime, err := timeFromSQL(createdTimeString)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error parsing container %s created time", id) 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.valid = true
ctr.runtime = runtime ctr.runtime = s.runtime
// Open and set the lockfile // Open and set the lockfile
lockPath := filepath.Join(lockDir, id) lockPath := filepath.Join(s.lockDir, id)
lock, err := storage.GetLockfile(lockPath) lock, err := storage.GetLockfile(lockPath)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error retrieving lockfile for container %s", id) 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 // Retrieve the spec from disk
ociSpec := new(spec.Spec) ociSpec := new(spec.Spec)
specPath := getSpecPath(specsDir, id) specPath := getSpecPath(s.specsDir, id)
fileContents, err := ioutil.ReadFile(specPath) fileContents, err := ioutil.ReadFile(specPath)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error reading container %s OCI spec", id) return nil, errors.Wrapf(err, "error reading container %s OCI spec", id)

View File

@ -21,7 +21,8 @@ func getTestContainer(id, name, locksDir string) (*Container, error) {
Name: name, Name: name,
RootfsImageID: id, RootfsImageID: id,
RootfsImageName: "testimg", RootfsImageName: "testimg",
UseImageConfig: true, ImageVolumes: true,
ReadOnly: true,
StaticDir: "/does/not/exist/", StaticDir: "/does/not/exist/",
Stdin: true, Stdin: true,
Labels: make(map[string]string), Labels: make(map[string]string),