summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpod/runtime.go19
-rw-r--r--libpod/sql_state.go1091
-rw-r--r--libpod/sql_state_internal.go1137
-rw-r--r--libpod/state_test.go30
4 files changed, 2 insertions, 2275 deletions
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 5c3705ee9..39c65548b 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -30,6 +30,7 @@ const (
// reboot
InMemoryStateStore RuntimeStateStore = iota
// SQLiteStateStore is a state backed by a SQLite database
+ // It is presently disabled
SQLiteStateStore RuntimeStateStore = iota
// BoltDBStateStore is a state backed by a BoltDB database
BoltDBStateStore RuntimeStateStore = iota
@@ -386,23 +387,7 @@ func makeRuntime(runtime *Runtime) error {
}
runtime.state = state
case SQLiteStateStore:
- dbPath := filepath.Join(runtime.config.StaticDir, "sql_state.db")
- specsDir := filepath.Join(runtime.config.StaticDir, "ocispec")
-
- // Make a directory to hold JSON versions of container OCI specs
- if err := os.MkdirAll(specsDir, 0755); err != nil {
- // The directory is allowed to exist
- if !os.IsExist(err) {
- return errors.Wrapf(err, "error creating runtime OCI specs directory %s",
- specsDir)
- }
- }
-
- state, err := NewSQLState(dbPath, specsDir, runtime.lockDir, runtime)
- if err != nil {
- return err
- }
- runtime.state = state
+ return errors.Wrapf(ErrInvalidArg, "SQLite state is currently disabled")
case BoltDBStateStore:
dbPath := filepath.Join(runtime.config.StaticDir, "bolt_state.db")
diff --git a/libpod/sql_state.go b/libpod/sql_state.go
deleted file mode 100644
index f583febda..000000000
--- a/libpod/sql_state.go
+++ /dev/null
@@ -1,1091 +0,0 @@
-package libpod
-
-import (
- "database/sql"
- "encoding/json"
- "os"
-
- "github.com/containernetworking/cni/pkg/types"
- cnitypes "github.com/containernetworking/cni/pkg/types/current"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
- // Use SQLite backend for sql package
- _ "github.com/mattn/go-sqlite3"
-)
-
-// DBSchema is the current DB schema version
-// Increments every time a change is made to the database's tables
-const DBSchema = 14
-
-// SQLState is a state implementation backed by a persistent SQLite3 database
-type SQLState struct {
- db *sql.DB
- specsDir string
- lockDir string
- runtime *Runtime
- valid bool
-}
-
-// NewSQLState initializes a SQL-backed state, created the database if necessary
-func NewSQLState(dbPath, specsDir, lockDir string, runtime *Runtime) (State, error) {
- state := new(SQLState)
-
- state.runtime = runtime
-
- // Make the directory that will hold JSON copies of container runtime specs
- if err := os.MkdirAll(specsDir, 0750); err != nil {
- // The directory is allowed to exist
- if !os.IsExist(err) {
- return nil, errors.Wrapf(err, "error creating OCI specs dir %s", specsDir)
- }
- }
- state.specsDir = specsDir
-
- // Make the directory that will hold container lockfiles
- if err := os.MkdirAll(lockDir, 0750); err != nil {
- // The directory is allowed to exist
- if !os.IsExist(err) {
- return nil, errors.Wrapf(err, "error creating lockfiles dir %s", lockDir)
- }
- }
- state.lockDir = lockDir
-
- // Open the database
- // Use loc=auto to get accurate locales for timestamps
- db, err := sql.Open("sqlite3", dbPath+"?_loc=auto")
- if err != nil {
- return nil, errors.Wrapf(err, "error opening database")
- }
-
- // Ensure connectivity
- if err := db.Ping(); err != nil {
- return nil, errors.Wrapf(err, "cannot establish connection to database")
- }
-
- // Prepare database
- if err := prepareDB(db); err != nil {
- return nil, err
- }
-
- // Ensure that the database matches our config
- if err := checkDB(db, runtime); err != nil {
- return nil, err
- }
-
- state.db = db
-
- state.valid = true
-
- return state, nil
-}
-
-// Close the state's database connection
-func (s *SQLState) Close() error {
- if !s.valid {
- return ErrDBClosed
- }
-
- s.valid = false
-
- if err := s.db.Close(); err != nil {
- return errors.Wrapf(err, "error closing database")
- }
-
- return nil
-}
-
-// Refresh clears the state after a reboot
-// Resets mountpoint, PID, state, netns path for all containers
-func (s *SQLState) Refresh() (err error) {
- const refresh = `UPDATE containerState SET
- State=?,
- Mountpoint=?,
- Pid=?,
- NetNSPath=?,
- ExecSessions=?,
- IPs=?,
- Routes=?,
- BindMounts=?;`
-
- if !s.valid {
- return ErrDBClosed
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to refresh state: %v", err2)
- }
- }
- }()
-
- // Refresh container state
- // The constants could be moved into the SQL, but keeping them here
- // will keep us in sync in case ContainerStateConfigured ever changes in
- // the container state
- _, err = tx.Exec(refresh,
- ContainerStateConfigured,
- "",
- 0,
- "",
- "{}",
- "[]",
- "[]",
- "{}")
- if err != nil {
- return errors.Wrapf(err, "error refreshing database state")
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction to refresh database")
- }
-
- return nil
-}
-
-// Container retrieves a container from its full ID
-func (s *SQLState) Container(id string) (*Container, error) {
- const query = containerQuery + "WHERE containers.Id=?;"
-
- if id == "" {
- return nil, ErrEmptyID
- }
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- row := s.db.QueryRow(query, id)
-
- ctr, err := s.ctrFromScannable(row)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving container %s from database", id)
- }
-
- return ctr, nil
-}
-
-// LookupContainer retrieves a container by full or unique partial ID or name
-func (s *SQLState) LookupContainer(idOrName string) (*Container, error) {
- const query = containerQuery + "WHERE (containers.Id LIKE ?) OR containers.Name=?;"
-
- if idOrName == "" {
- return nil, ErrEmptyID
- }
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- rows, err := s.db.Query(query, idOrName+"%", idOrName)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving container %s row from database", idOrName)
- }
- defer rows.Close()
-
- foundResult := false
- var ctr *Container
- for rows.Next() {
- if foundResult {
- return nil, errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
- }
-
- var err error
- ctr, err = s.ctrFromScannable(rows)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving container %s from database", idOrName)
- }
- foundResult = true
- }
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving rows for container ID or name %s", idOrName)
- }
-
- if !foundResult {
- return nil, errors.Wrapf(ErrNoSuchCtr, "no container with ID or name %s found", idOrName)
- }
-
- return ctr, nil
-}
-
-// HasContainer checks if the given container is present in the state
-// It accepts a full ID
-func (s *SQLState) HasContainer(id string) (bool, error) {
- const query = "SELECT 1 FROM containers WHERE Id=?;"
-
- if id == "" {
- return false, ErrEmptyID
- }
-
- if !s.valid {
- return false, ErrDBClosed
- }
-
- row := s.db.QueryRow(query, id)
-
- var check int
- err := row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- return false, nil
- }
-
- return false, errors.Wrapf(err, "error questing database for existence of container %s", id)
- } else if check != 1 {
- return false, errors.Wrapf(ErrInternal, "check digit for HasContainer query incorrect")
- }
-
- return true, nil
-}
-
-// AddContainer adds the given container to the state
-// If the container belongs to a pod, that pod must already be present in the
-// state, and the container will be added to the pod
-func (s *SQLState) AddContainer(ctr *Container) (err error) {
- if !ctr.valid {
- return ErrCtrRemoved
- }
-
- if ctr.config.Pod != "" {
- return errors.Wrapf(ErrPodExists, "cannot add container that belongs to a pod, use AddContainerToPod instead")
- }
-
- return s.addContainer(ctr, nil)
-}
-
-// UpdateContainer updates a container's state from the database
-func (s *SQLState) UpdateContainer(ctr *Container) error {
- const query = `SELECT State,
- ConfigPath,
- RunDir,
- Mountpoint,
- StartedTime,
- FinishedTime,
- ExitCode,
- OomKilled,
- Pid,
- NetNSPath,
- ExecSessions,
- IPs,
- Routes,
- BindMounts
- FROM containerState WHERE ID=?;`
-
- var (
- state int
- configPath string
- runDir string
- mountpoint string
- startedTimeString string
- finishedTimeString string
- exitCode int32
- oomKilled int
- pid int
- netNSPath string
- execSessions string
- ipsJSON string
- routesJSON string
- bindMountsJSON string
- )
-
- if !s.valid {
- return ErrDBClosed
- }
-
- if !ctr.valid {
- return ErrCtrRemoved
- }
-
- row := s.db.QueryRow(query, ctr.ID())
- err := row.Scan(
- &state,
- &configPath,
- &runDir,
- &mountpoint,
- &startedTimeString,
- &finishedTimeString,
- &exitCode,
- &oomKilled,
- &pid,
- &netNSPath,
- &execSessions,
- &ipsJSON,
- &routesJSON,
- &bindMountsJSON)
- if err != nil {
- // The container may not exist in the database
- if err == sql.ErrNoRows {
- // Assume that the container was removed by another process
- // As such make it invalid
- ctr.valid = false
-
- return errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found in database", ctr.ID())
- }
-
- return errors.Wrapf(err, "error parsing database state for container %s", ctr.ID())
- }
-
- newState := new(containerState)
- newState.State = ContainerStatus(state)
- newState.ConfigPath = configPath
- newState.RunDir = runDir
- newState.Mountpoint = mountpoint
- newState.ExitCode = exitCode
- newState.OOMKilled = boolFromSQL(oomKilled)
- newState.PID = pid
-
- newState.ExecSessions = make(map[string]*ExecSession)
- if err := json.Unmarshal([]byte(execSessions), &newState.ExecSessions); err != nil {
- return errors.Wrapf(err, "error parsing container %s exec sessions", ctr.ID())
- }
-
- ips := []*cnitypes.IPConfig{}
- if err := json.Unmarshal([]byte(ipsJSON), &ips); err != nil {
- return errors.Wrapf(err, "error parsing container %s IPs JSON", ctr.ID())
- }
- if len(ips) > 0 {
- newState.IPs = ips
- }
-
- routes := []*types.Route{}
- if err := json.Unmarshal([]byte(routesJSON), &routes); err != nil {
- return errors.Wrapf(err, "error parsing container %s routes JSON", ctr.ID())
- }
- if len(routes) > 0 {
- newState.Routes = routes
- }
-
- bindMounts := make(map[string]string)
- if err := json.Unmarshal([]byte(bindMountsJSON), &bindMounts); err != nil {
- return errors.Wrapf(err, "error parsing container %s bind mounts JSON", ctr.ID())
- }
- newState.BindMounts = bindMounts
-
- if newState.Mountpoint != "" {
- newState.Mounted = true
- }
-
- startedTime, err := timeFromSQL(startedTimeString)
- if err != nil {
- return errors.Wrapf(err, "error parsing container %s started time", ctr.ID())
- }
- newState.StartedTime = startedTime
-
- finishedTime, err := timeFromSQL(finishedTimeString)
- if err != nil {
- return errors.Wrapf(err, "error parsing container %s finished time", ctr.ID())
- }
- 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
-
- return nil
-}
-
-// SaveContainer updates a container's state in the database
-func (s *SQLState) SaveContainer(ctr *Container) (err error) {
- const update = `UPDATE containerState SET
- State=?,
- ConfigPath=?,
- RunDir=?,
- Mountpoint=?,
- StartedTime=?,
- FinishedTime=?,
- ExitCode=?,
- OomKilled=?,
- Pid=?,
- NetNSPath=?,
- ExecSessions=?,
- IPs=?,
- Routes=?,
- BindMounts=?
- WHERE Id=?;`
-
- if !ctr.valid {
- return ErrCtrRemoved
- }
-
- execSessionsJSON, err := json.Marshal(ctr.state.ExecSessions)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s exec sessions", ctr.ID())
- }
-
- netNSPath := ""
- if ctr.state.NetNS != nil {
- netNSPath = ctr.state.NetNS.Path()
- }
-
- ipsJSON, err := json.Marshal(ctr.state.IPs)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s IPs to JSON", ctr.ID())
- }
-
- routesJSON, err := json.Marshal(ctr.state.Routes)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s routes to JSON", ctr.ID())
- }
-
- bindMountsJSON, err := json.Marshal(ctr.state.BindMounts)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s bind mounts to JSON", ctr.ID())
- }
-
- if !s.valid {
- return ErrDBClosed
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
- }
- }
- }()
-
- // Add container state to the database
- result, err := tx.Exec(update,
- ctr.state.State,
- ctr.state.ConfigPath,
- ctr.state.RunDir,
- ctr.state.Mountpoint,
- timeToSQL(ctr.state.StartedTime),
- timeToSQL(ctr.state.FinishedTime),
- ctr.state.ExitCode,
- boolToSQL(ctr.state.OOMKilled),
- ctr.state.PID,
- netNSPath,
- execSessionsJSON,
- ipsJSON,
- routesJSON,
- bindMountsJSON,
- ctr.ID())
- if err != nil {
- return errors.Wrapf(err, "error updating container %s state in database", ctr.ID())
- }
- rows, err := result.RowsAffected()
- if err != nil {
- return errors.Wrapf(err, "error retrieving number of rows modified by update of container %s", ctr.ID())
- }
- if rows == 0 {
- // Container was probably removed elsewhere
- ctr.valid = false
- return ErrNoSuchCtr
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction to update container %s", ctr.ID())
- }
-
- return nil
-}
-
-// ContainerInUse checks if other containers depend on the given container
-// It returns the IDs of containers which depend on the given container
-func (s *SQLState) ContainerInUse(ctr *Container) ([]string, error) {
- const inUseQuery = `SELECT Id FROM containers WHERE
- IPCNsCtr=? OR
- MountNsCtr=? OR
- NetNsCtr=? OR
- PIDNsCtr=? OR
- UserNsCtr=? OR
- UTSNsCtr=? OR
- CgroupNsCtr=?;`
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- if !ctr.valid {
- return nil, ErrCtrRemoved
- }
-
- id := ctr.ID()
-
- rows, err := s.db.Query(inUseQuery, id, id, id, id, id, id, id)
- if err != nil {
- return nil, errors.Wrapf(err, "error querying database for containers that depend on container %s", id)
- }
- defer rows.Close()
-
- ids := []string{}
-
- for rows.Next() {
- var ctrID string
- if err := rows.Scan(&ctrID); err != nil {
- return nil, errors.Wrapf(err, "error scanning container IDs from db rows for container %s", id)
- }
-
- ids = append(ids, ctrID)
- }
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving rows for container %s", id)
- }
-
- return ids, nil
-}
-
-// RemoveContainer removes the given container from the state
-func (s *SQLState) RemoveContainer(ctr *Container) error {
- if ctr.config.Pod != "" {
- return errors.Wrapf(ErrPodExists, "container %s belongs to a pod, use RemoveContainerFromPod", ctr.ID())
- }
-
- return s.removeContainer(ctr, nil)
-}
-
-// AllContainers retrieves all the containers presently in the state
-func (s *SQLState) AllContainers() ([]*Container, error) {
- const query = containerQuery + ";"
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- rows, err := s.db.Query(query)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving containers from database")
- }
- defer rows.Close()
-
- containers := []*Container{}
-
- for rows.Next() {
- ctr, err := s.ctrFromScannable(rows)
- if err != nil {
- return nil, err
- }
-
- containers = append(containers, ctr)
- }
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving container rows")
- }
-
- return containers, nil
-}
-
-// Pod retrieves a pod by its full ID
-func (s *SQLState) Pod(id string) (*Pod, error) {
- const query = "SELECT * FROM pods WHERE Id=?;"
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- if id == "" {
- return nil, ErrEmptyID
- }
-
- row := s.db.QueryRow(query, id)
-
- pod, err := s.podFromScannable(row)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving pod %s from database", id)
- }
-
- return pod, nil
-}
-
-// LookupPod retrieves a pot by full or unique partial ID or name
-func (s *SQLState) LookupPod(idOrName string) (*Pod, error) {
- const query = "SELECT * FROM pods WHERE (Id LIKE ?) OR Name=?;"
-
- if idOrName == "" {
- return nil, ErrEmptyID
- }
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- rows, err := s.db.Query(query, idOrName+"%", idOrName)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving pod %s row from database", idOrName)
- }
- defer rows.Close()
-
- foundResult := false
- var pod *Pod
- for rows.Next() {
- if foundResult {
- return nil, errors.Wrapf(ErrCtrExists, "more than one result for ID or name %s", idOrName)
- }
-
- var err error
- pod, err = s.podFromScannable(rows)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving pod %s from database", idOrName)
- }
- foundResult = true
- }
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving rows for pod ID or name %s", idOrName)
- }
-
- if !foundResult {
- return nil, errors.Wrapf(ErrNoSuchCtr, "no pod with ID or name %s found", idOrName)
- }
-
- return pod, nil
-}
-
-// HasPod checks if a pod exists given its full ID
-func (s *SQLState) HasPod(id string) (bool, error) {
- if id == "" {
- return false, ErrEmptyID
- }
-
- if !s.valid {
- return false, ErrDBClosed
- }
-
- return s.podExists(id)
-}
-
-// PodHasContainer checks if the given pod containers a container with the given
-// ID
-func (s *SQLState) PodHasContainer(pod *Pod, ctrID string) (bool, error) {
- const query = "SELECT 1 FROM containers WHERE Id=? AND Pod=?;"
-
- if ctrID == "" {
- return false, ErrEmptyID
- }
-
- if !s.valid {
- return false, ErrDBClosed
- }
-
- if !pod.valid {
- return false, ErrPodRemoved
- }
-
- row := s.db.QueryRow(query, ctrID, pod.ID())
-
- var check int
- err := row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- return false, nil
- }
-
- return false, errors.Wrapf(err, "error questing database for existence of container %s", ctrID)
- } else if check != 1 {
- return false, errors.Wrapf(ErrInternal, "check digit for PodHasContainer query incorrect")
- }
-
- return true, nil
-}
-
-// PodContainersByID returns the container IDs of all containers in the given
-// pod
-func (s *SQLState) PodContainersByID(pod *Pod) ([]string, error) {
- const query = "SELECT Id FROM containers WHERE Pod=?;"
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- if !pod.valid {
- return nil, ErrPodRemoved
- }
-
- // Check to make sure pod still exists in DB
- exists, err := s.podExists(pod.ID())
- if err != nil {
- return nil, err
- }
- if !exists {
- pod.valid = false
- return nil, ErrPodRemoved
- }
-
- // Get actual containers
- rows, err := s.db.Query(query, pod.ID())
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving containers from database")
- }
- defer rows.Close()
-
- containers := []string{}
-
- for rows.Next() {
- var id string
- if err := rows.Scan(&id); err != nil {
- if err == sql.ErrNoRows {
- return nil, ErrNoSuchCtr
- }
-
- return nil, errors.Wrapf(err, "error parsing database row into container ID")
- }
-
- containers = append(containers, id)
- }
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving container rows")
- }
-
- return containers, nil
-}
-
-// PodContainers returns all the containers in a pod given the pod's full ID
-func (s *SQLState) PodContainers(pod *Pod) ([]*Container, error) {
- const query = containerQuery + "WHERE containers.Pod=?;"
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- if !pod.valid {
- return nil, ErrPodRemoved
- }
-
- // Check to make sure pod still exists in DB
- exists, err := s.podExists(pod.ID())
- if err != nil {
- return nil, err
- }
- if !exists {
- pod.valid = false
- return nil, ErrPodRemoved
- }
-
- // Get actual containers
- rows, err := s.db.Query(query, pod.ID())
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving containers from database")
- }
- defer rows.Close()
-
- containers := []*Container{}
-
- for rows.Next() {
- ctr, err := s.ctrFromScannable(rows)
- if err != nil {
- return nil, err
- }
-
- containers = append(containers, ctr)
- }
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving container rows")
- }
-
- return containers, nil
-}
-
-// AddPod adds a pod to the state
-// Only empty pods can be added to the state
-func (s *SQLState) AddPod(pod *Pod) (err error) {
- const (
- podQuery = "INSERT INTO pods VALUES (?, ?, ?);"
- registryQuery = "INSERT INTO registry VALUES (?, ?);"
- )
-
- if !s.valid {
- return ErrDBClosed
- }
-
- if !pod.valid {
- return ErrPodRemoved
- }
-
- labelsJSON, err := json.Marshal(pod.config.Labels)
- if err != nil {
- return errors.Wrapf(err, "error marshaling pod %s labels to JSON", pod.ID())
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to add pod %s: %v", pod.ID(), err2)
- }
- }
- }()
-
- if _, err := tx.Exec(registryQuery, pod.ID(), pod.Name()); err != nil {
- return errors.Wrapf(err, "error adding pod %s to name/ID registry", pod.ID())
- }
-
- if _, err = tx.Exec(podQuery, pod.ID(), pod.Name(), string(labelsJSON)); err != nil {
- return errors.Wrapf(err, "error adding pod %s to database", pod.ID())
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction to add pod %s", pod.ID())
- }
-
- return nil
-}
-
-// RemovePod removes a pod from the state
-// Only empty pods can be removed
-func (s *SQLState) RemovePod(pod *Pod) (err error) {
- const (
- removePod = "DELETE FROM pods WHERE ID=?;"
- removeRegistry = "DELETE FROM registry WHERE Id=?;"
- )
-
- if !s.valid {
- return ErrDBClosed
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to remove pod %s: %v", pod.ID(), err2)
- }
- }
- }()
-
- // Check rows acted on for the first statement, verify we actually removed something
- result, err := tx.Exec(removePod, pod.ID())
- if err != nil {
- return errors.Wrapf(err, "error removing pod %s from pods table", pod.ID())
- }
- rows, err := result.RowsAffected()
- if err != nil {
- return errors.Wrapf(err, "error retrieving number of rows in transaction removing pod %s", pod.ID())
- } else if rows == 0 {
- pod.valid = false
- return ErrNoSuchPod
- }
-
- // We know it exists, remove it from registry
- if _, err := tx.Exec(removeRegistry, pod.ID()); err != nil {
- return errors.Wrapf(err, "error removing pod %s from name/ID registry", pod.ID())
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction to remove pod %s", pod.ID())
- }
-
- return nil
-}
-
-// RemovePodContainers removes all containers in a pod simultaneously
-// This can avoid issues with dependencies within the pod
-// The operation will fail if any container in the pod has a dependency from
-// outside the pod
-func (s *SQLState) RemovePodContainers(pod *Pod) (err error) {
- const (
- getPodCtrs = "SELECT Id FROM containers WHERE pod=?;"
- removeCtr = "DELETE FROM containers WHERE pod=?;"
- removeCtrState = "DELETE FROM containerState WHERE ID IN (SELECT Id FROM containers WHERE pod=?);"
- )
-
- if !s.valid {
- return ErrDBClosed
- }
-
- if !pod.valid {
- return ErrPodRemoved
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to remove pod %s containers: %v", pod.ID(), err2)
- }
- }
- }()
-
- // Check if the pod exists
- exists, err := podExistsTx(pod.ID(), tx)
- if err != nil {
- return err
- }
- if !exists {
- pod.valid = false
- return ErrNoSuchPod
- }
-
- // First get all containers in the pod
- rows, err := tx.Query(getPodCtrs, pod.ID())
- if err != nil {
- return errors.Wrapf(err, "error retrieving containers from database")
- }
- defer rows.Close()
-
- containers := []string{}
-
- for rows.Next() {
- var id string
- if err := rows.Scan(&id); err != nil {
- if err == sql.ErrNoRows {
- return ErrNoSuchCtr
- }
-
- return errors.Wrapf(err, "error parsing database row into container ID")
- }
-
- containers = append(containers, id)
- }
- if err := rows.Err(); err != nil {
- return errors.Wrapf(err, "error retrieving container rows")
- }
-
- // Have container IDs, now exec SQL to remove containers in the pod
- // Remove state first, as it needs the subquery on containers
- // Don't bother checking if we actually removed anything, we just want to
- // empty the pod
- if _, err := tx.Exec(removeCtrState, pod.ID()); err != nil {
- return errors.Wrapf(err, "error removing pod %s containers from state table", pod.ID())
- }
-
- if _, err := tx.Exec(removeCtr, pod.ID()); err != nil {
- return errors.Wrapf(err, "error removing pod %s containers from containers table", pod.ID())
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction remove pod %s containers", pod.ID())
- }
-
- // Remove JSON files from the containers in question
- hasError := false
- for _, ctr := range containers {
- jsonPath := getSpecPath(s.specsDir, ctr)
- if err := os.Remove(jsonPath); err != nil {
- logrus.Errorf("Error removing spec JSON for container %s: %v", ctr, err)
- hasError = true
- }
-
- portsPath := getPortsPath(s.specsDir, ctr)
- if err := os.Remove(portsPath); err != nil {
- if !os.IsNotExist(err) {
- logrus.Errorf("Error removing ports JSON for container %s: %v", ctr, err)
- hasError = true
- }
- }
- }
- if hasError {
- return errors.Wrapf(ErrInternal, "error removing JSON state for some containers")
- }
-
- return nil
-}
-
-// AddContainerToPod adds a container to the given pod
-func (s *SQLState) AddContainerToPod(pod *Pod, ctr *Container) error {
- if !pod.valid {
- return ErrPodRemoved
- }
-
- if !ctr.valid {
- return ErrCtrRemoved
- }
-
- if ctr.config.Pod != pod.ID() {
- return errors.Wrapf(ErrInvalidArg, "container's pod ID does not match given pod's ID")
- }
-
- return s.addContainer(ctr, pod)
-}
-
-// RemoveContainerFromPod removes a container from the given pod
-func (s *SQLState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
- if ctr.config.Pod != pod.ID() {
- return errors.Wrapf(ErrInvalidArg, "container %s is not in pod %s", ctr.ID(), pod.ID())
- }
-
- return s.removeContainer(ctr, pod)
-}
-
-// AllPods retrieves all pods presently in the state
-func (s *SQLState) AllPods() ([]*Pod, error) {
- const query = "SELECT * FROM pods;"
-
- if !s.valid {
- return nil, ErrDBClosed
- }
-
- rows, err := s.db.Query(query)
- if err != nil {
- return nil, errors.Wrapf(err, "error querying database for all pods")
- }
- defer rows.Close()
-
- pods := []*Pod{}
-
- for rows.Next() {
- pod, err := s.podFromScannable(rows)
- if err != nil {
- return nil, err
- }
-
- pods = append(pods, pod)
- }
-
- if err := rows.Err(); err != nil {
- return nil, errors.Wrapf(err, "error retrieving pod rows")
- }
-
- return pods, nil
-}
diff --git a/libpod/sql_state_internal.go b/libpod/sql_state_internal.go
deleted file mode 100644
index 7729fb6ec..000000000
--- a/libpod/sql_state_internal.go
+++ /dev/null
@@ -1,1137 +0,0 @@
-package libpod
-
-import (
- "database/sql"
- "encoding/json"
- "io/ioutil"
- "os"
- "path/filepath"
- "time"
-
- "github.com/containernetworking/cni/pkg/types"
- cnitypes "github.com/containernetworking/cni/pkg/types/current"
- "github.com/containers/storage"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-
- // Use SQLite backend for sql package
- _ "github.com/mattn/go-sqlite3"
-)
-
-const (
- // Basic structure of a query to retrieve a container
- // Just append optional WHERE clauses and end with a semicolon
- // Contains trailing whitespace to ensure we can append without issue
- containerQuery = `SELECT containers.*,
- containerState.State,
- containerState.ConfigPath,
- containerState.RunDir,
- containerState.MountPoint,
- containerState.StartedTime,
- containerState.FinishedTime,
- containerState.ExitCode,
- containerState.OomKilled,
- containerState.Pid,
- containerState.NetNSPath,
- containerState.ExecSessions,
- containerState.IPs,
- containerState.Routes,
- containerState.BindMounts
- FROM containers
- INNER JOIN
- containerState ON containers.Id = containerState.Id `
- // ExistsQuery is a query to check if a pod exists
- ExistsQuery = "SELECT 1 FROM pods WHERE Id=?;"
-)
-
-// Checks that the DB configuration matches the runtime's configuration
-func checkDB(db *sql.DB, r *Runtime) (err error) {
- // Create a table to hold runtime information
- // 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,
- CHECK (Id=0)
- );
- `
- const fillRuntimeTable = `INSERT INTO runtime VALUES (
- ?, ?, ?, ?, ?, ?, ?
- );`
-
- const selectRuntimeTable = `SELECT SchemaVersion,
- StaticDir,
- TmpDir,
- RunRoot,
- GraphRoot,
- GraphDriverName
- FROM runtime WHERE id=0;`
-
- const checkRuntimeExists = "SELECT name FROM sqlite_master WHERE type='table' AND name='runtime';"
-
- committed := false
-
- tx, err := db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to check runtime table: %v", err2)
- }
- }
-
- }()
-
- row := tx.QueryRow(checkRuntimeExists)
- var table string
- if err := row.Scan(&table); err != nil {
- // There is no runtime table
- // Create and populate the runtime table
- if err == sql.ErrNoRows {
- if _, err := tx.Exec(runtimeTable); err != nil {
- return errors.Wrapf(err, "error creating runtime table in database")
- }
-
- _, err := tx.Exec(fillRuntimeTable,
- 0,
- DBSchema,
- r.config.StaticDir,
- r.config.TmpDir,
- r.config.StorageConfig.RunRoot,
- r.config.StorageConfig.GraphRoot,
- r.config.StorageConfig.GraphDriverName)
- if err != nil {
- return errors.Wrapf(err, "error populating runtime table in database")
- }
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing runtime table transaction in database")
- }
-
- return nil
- }
-
- return errors.Wrapf(err, "error checking for presence of runtime table in database")
- }
-
- // There is a runtime table
- // Retrieve its contents
- var (
- schemaVersion int
- staticDir string
- tmpDir string
- runRoot string
- graphRoot string
- graphDriverName string
- )
-
- row = tx.QueryRow(selectRuntimeTable)
- err = row.Scan(
- &schemaVersion,
- &staticDir,
- &tmpDir,
- &runRoot,
- &graphRoot,
- &graphDriverName)
- if err != nil {
- return errors.Wrapf(err, "error retrieving runtime information from database")
- }
-
- // Compare the information in the database against our runtime config
- if schemaVersion != DBSchema {
- return errors.Wrapf(ErrDBBadConfig, "database schema version %d does not match our schema version %d",
- schemaVersion, DBSchema)
- }
- if staticDir != r.config.StaticDir {
- return errors.Wrapf(ErrDBBadConfig, "database static directory %s does not match our static directory %s",
- staticDir, r.config.StaticDir)
- }
- if tmpDir != r.config.TmpDir {
- return errors.Wrapf(ErrDBBadConfig, "database temp directory %s does not match our temp directory %s",
- tmpDir, r.config.TmpDir)
- }
- if runRoot != r.config.StorageConfig.RunRoot {
- return errors.Wrapf(ErrDBBadConfig, "database runroot directory %s does not match our runroot directory %s",
- runRoot, r.config.StorageConfig.RunRoot)
- }
- if graphRoot != r.config.StorageConfig.GraphRoot {
- return errors.Wrapf(ErrDBBadConfig, "database graph root directory %s does not match our graph root directory %s",
- graphRoot, r.config.StorageConfig.GraphRoot)
- }
- if graphDriverName != r.config.StorageConfig.GraphDriverName {
- return errors.Wrapf(ErrDBBadConfig, "database runroot directory %s does not match our runroot directory %s",
- graphDriverName, r.config.StorageConfig.GraphDriverName)
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing runtime table transaction in database")
- }
-
- return nil
-}
-
-// Performs database setup including by not limited to initializing tables in
-// the database
-func prepareDB(db *sql.DB) (err error) {
- // 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 {
- return errors.Wrapf(err, "error enabling foreign key support in database")
- }
-
- // Create a table for holding container and pod names and IDs
- const createRegistry = `
- CREATE TABLE IF NOT EXISTS registry(
- Id TEXT NOT NULL PRIMARY KEY,
- Name TEXT NOT NULL UNIQUE
- );
- `
-
- // 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,
- Pod TEXT,
-
- RootfsImageID TEXT NOT NULL,
- RootfsImageName TEXT NOT NULL,
- ImageVolumes INTEGER NOT NULL,
- ShmDir TEXT NOT NULL,
- ShmSize INTEGER NOT NULL,
- StaticDir TEXT NOT NULL,
- Mounts TEXT NOT NULL,
- LogPath TEXT NOT NULL,
-
- Privileged INTEGER 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,
- CgroupNsCtr TEXT,
-
- CreateNetNS INTEGER NOT NULL,
- DNSServer TEXT NOT NULL,
- DNSSearch TEXT NOT NULL,
- DNSOption TEXT NOT NULL,
- HostAdd 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 (SHMSize>=0),
- CHECK (Privileged IN (0, 1)),
- CHECK (CreateNetNS IN (0, 1)),
- CHECK (Stdin IN (0, 1)),
- CHECK (StopSignal>=0),
- FOREIGN KEY (Id) REFERENCES registry(Id) DEFERRABLE INITIALLY DEFERRED,
- FOREIGN KEY (Id) REFERENCES containerState(Id) DEFERRABLE INITIALLY DEFERRED,
- FOREIGN KEY (Name) REFERENCES registry(Name) DEFERRABLE INITIALLY DEFERRED,
- FOREIGN KEY (Pod) REFERENCES pods(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,
- FOREIGN KEY (CgroupNsCtr) 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,
- ExecSessions TEXT NOT NULL,
- IPs TEXT NOT NULL,
- Routes TEXT NOT NULL,
- BindMounts TEXT NOT NULL,
-
- CHECK (State>0),
- CHECK (OomKilled IN (0, 1)),
- FOREIGN KEY (Id) REFERENCES registry(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 pods(
- Id TEXT NOT NULL PRIMARY KEY,
- Name TEXT NOT NULL UNIQUE,
- Labels TEXT NOT NULL,
- FOREIGN KEY (Id) REFERENCES registry(Id) DEFERRABLE INITIALLY DEFERRED,
- FOREIGN KEY (Name) REFERENCES registry(Name) DEFERRABLE INITIALLY DEFERRED
- );
- `
-
- committed := false
-
- // Create the tables
- tx, err := db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to create tables: %v", err2)
- }
- }
-
- }()
-
- if _, err := tx.Exec(createRegistry); err != nil {
- return errors.Wrapf(err, "error creating ID and Name registry in database")
- }
- if _, err := tx.Exec(createCtr); err != nil {
- return errors.Wrapf(err, "error creating containers table in database")
- }
- 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")
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing table creation transaction in database")
- }
-
- return nil
-}
-
-// Check if given pod exists
-// Internal-only version of hasPod
-func (s *SQLState) podExists(id string) (bool, error) {
- row := s.db.QueryRow(ExistsQuery, id)
-
- var check int
- err := row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- return false, nil
- }
-
- return false, errors.Wrapf(err, "error questing database for existence of pod %s", id)
- } else if check != 1 {
- return false, errors.Wrapf(ErrInternal, "check digit for podExists query incorrect")
- }
-
- return true, nil
-}
-
-// Check if given pod exists within a transaction
-// Transaction-based version of podExists
-func podExistsTx(id string, tx *sql.Tx) (bool, error) {
- row := tx.QueryRow(ExistsQuery, id)
-
- var check int
- err := row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- return false, nil
- }
-
- return false, errors.Wrapf(err, "error questing database for existence of pod %s", id)
- } else if check != 1 {
- return false, errors.Wrapf(ErrInternal, "check digit for podExists query incorrect")
- }
-
- return true, nil
-}
-
-// Get filename for OCI spec on disk
-func getSpecPath(specsDir, id string) string {
- return filepath.Join(specsDir, id)
-}
-
-// Get filename for container port mappings on disk
-func getPortsPath(specsDir, id string) string {
- return filepath.Join(specsDir, id+"_ports")
-}
-
-// Convert a bool into SQL-readable format
-func boolToSQL(b bool) int {
- if b {
- return 1
- }
-
- 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
-}
-
-// Convert a time.Time into SQL-readable format
-func timeToSQL(t time.Time) string {
- return t.Format(time.RFC3339Nano)
-}
-
-// Convert a SQL-readable time back to a time.Time
-func timeFromSQL(s string) (time.Time, error) {
- return time.Parse(time.RFC3339Nano, s)
-}
-
-// Interface to abstract sql.Rows and sql.Row so they can both be used
-type scannable interface {
- Scan(dest ...interface{}) error
-}
-
-// Read a single container from a single row result in the database
-func (s *SQLState) ctrFromScannable(row scannable) (*Container, error) {
- var (
- id string
- name string
- pod sql.NullString
-
- rootfsImageID string
- rootfsImageName string
- imageVolumes int
- shmDir string
- shmSize int64
- staticDir string
- mounts string
- logPath string
-
- privileged int
- processLabel string
- mountLabel string
- user string
-
- ipcNsCtrNullStr sql.NullString
- mountNsCtrNullStr sql.NullString
- netNsCtrNullStr sql.NullString
- pidNsCtrNullStr sql.NullString
- userNsCtrNullStr sql.NullString
- utsNsCtrNullStr sql.NullString
- cgroupNsCtrNullStr sql.NullString
-
- createNetNS int
- dnsServerJSON string
- dnsSearchJSON string
- dnsOptionJSON string
- hostAddJSON string
-
- stdin int
- labelsJSON string
- stopSignal uint
- stopTimeout uint
- createdTimeString string
- cgroupParent string
-
- state int
- configPath string
- runDir string
- mountpoint string
- startedTimeString string
- finishedTimeString string
- exitCode int32
- oomKilled int
- pid int
- netNSPath string
- execSessions string
- ipsJSON string
- routesJSON string
- bindMountsJSON string
- )
-
- err := row.Scan(
- &id,
- &name,
- &pod,
-
- &rootfsImageID,
- &rootfsImageName,
- &imageVolumes,
- &shmDir,
- &shmSize,
- &staticDir,
- &mounts,
- &logPath,
-
- &privileged,
- &processLabel,
- &mountLabel,
- &user,
-
- &ipcNsCtrNullStr,
- &mountNsCtrNullStr,
- &netNsCtrNullStr,
- &pidNsCtrNullStr,
- &userNsCtrNullStr,
- &utsNsCtrNullStr,
- &cgroupNsCtrNullStr,
-
- &createNetNS,
- &dnsServerJSON,
- &dnsSearchJSON,
- &dnsOptionJSON,
- &hostAddJSON,
-
- &stdin,
- &labelsJSON,
- &stopSignal,
- &stopTimeout,
- &createdTimeString,
- &cgroupParent,
-
- &state,
- &configPath,
- &runDir,
- &mountpoint,
- &startedTimeString,
- &finishedTimeString,
- &exitCode,
- &oomKilled,
- &pid,
- &netNSPath,
- &execSessions,
- &ipsJSON,
- &routesJSON,
- &bindMountsJSON)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, ErrNoSuchCtr
- }
-
- return nil, errors.Wrapf(err, "error parsing database row into container")
- }
-
- ctr := new(Container)
- ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
-
- ctr.config.ID = id
- ctr.config.Name = name
- ctr.config.Pod = stringFromNullString(pod)
-
- ctr.config.RootfsImageID = rootfsImageID
- ctr.config.RootfsImageName = rootfsImageName
- ctr.config.ImageVolumes = boolFromSQL(imageVolumes)
- ctr.config.ShmDir = shmDir
- ctr.config.ShmSize = shmSize
- ctr.config.StaticDir = staticDir
- ctr.config.LogPath = logPath
-
- ctr.config.Privileged = boolFromSQL(privileged)
- ctr.config.ProcessLabel = processLabel
- ctr.config.MountLabel = mountLabel
- 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.CgroupNsCtr = stringFromNullString(cgroupNsCtrNullStr)
-
- ctr.config.CreateNetNS = boolFromSQL(createNetNS)
-
- ctr.config.Stdin = boolFromSQL(stdin)
- ctr.config.StopSignal = stopSignal
- ctr.config.StopTimeout = stopTimeout
- ctr.config.CgroupParent = cgroupParent
-
- ctr.state.State = ContainerStatus(state)
- ctr.state.ConfigPath = configPath
- ctr.state.RunDir = runDir
- ctr.state.Mountpoint = mountpoint
- ctr.state.ExitCode = exitCode
- ctr.state.OOMKilled = boolFromSQL(oomKilled)
- ctr.state.PID = pid
-
- // TODO should we store this in the database separately instead?
- if ctr.state.Mountpoint != "" {
- ctr.state.Mounted = true
- }
-
- if err := json.Unmarshal([]byte(mounts), &ctr.config.Mounts); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s mounts JSON", id)
- }
-
- if err := json.Unmarshal([]byte(dnsServerJSON), &ctr.config.DNSServer); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s DNS server JSON", id)
- }
-
- if err := json.Unmarshal([]byte(dnsSearchJSON), &ctr.config.DNSSearch); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s DNS search JSON", id)
- }
-
- if err := json.Unmarshal([]byte(dnsOptionJSON), &ctr.config.DNSOption); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s DNS option JSON", id)
- }
-
- if err := json.Unmarshal([]byte(hostAddJSON), &ctr.config.HostAdd); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s DNS server JSON", id)
- }
-
- ctr.state.ExecSessions = make(map[string]*ExecSession)
- if err := json.Unmarshal([]byte(execSessions), &ctr.state.ExecSessions); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s exec sessions JSON", id)
- }
-
- ips := []*cnitypes.IPConfig{}
- if err := json.Unmarshal([]byte(ipsJSON), &ips); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s IP addresses JSON", id)
- }
- if len(ips) > 0 {
- ctr.state.IPs = ips
- }
-
- routes := []*types.Route{}
- if err := json.Unmarshal([]byte(routesJSON), &routes); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s routes JSON", id)
- }
- if len(routes) > 0 {
- ctr.state.Routes = routes
- }
-
- bindMounts := make(map[string]string)
- if err := json.Unmarshal([]byte(bindMountsJSON), &bindMounts); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s bind mounts JSON", id)
- }
- ctr.state.BindMounts = bindMounts
-
- 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)
- }
- ctr.config.CreatedTime = createdTime
-
- startedTime, err := timeFromSQL(startedTimeString)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s started time", id)
- }
- ctr.state.StartedTime = startedTime
-
- finishedTime, err := timeFromSQL(finishedTimeString)
- if err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s finished time", id)
- }
- 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 = s.runtime
-
- // Open and set the lockfile
- 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)
- }
- ctr.lock = lock
-
- // Retrieve the spec from disk
- ociSpec := new(spec.Spec)
- 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)
- }
- if err := json.Unmarshal(fileContents, ociSpec); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s OCI spec", id)
- }
- ctr.config.Spec = ociSpec
-
- // Retrieve the ports from disk
- // They may not exist - if they don't, this container just doesn't have ports
- portPath := getPortsPath(s.specsDir, id)
- if _, err = os.Stat(portPath); err != nil {
- if !os.IsNotExist(err) {
- return nil, errors.Wrapf(err, "error stating container %s JSON ports", id)
- }
- }
- if err == nil {
- // The file exists, read it
- fileContents, err := ioutil.ReadFile(portPath)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading container %s JSON ports", id)
- }
- if err := json.Unmarshal(fileContents, &ctr.config.PortMappings); err != nil {
- return nil, errors.Wrapf(err, "error parsing container %s JSON ports", id)
- }
- }
-
- return ctr, nil
-}
-
-// Read a single pod from a single row result in the database
-func (s *SQLState) podFromScannable(row scannable) (*Pod, error) {
- var (
- id string
- name string
- labelsJSON string
- )
-
- err := row.Scan(&id, &name, &labelsJSON)
- if err != nil {
- if err == sql.ErrNoRows {
- return nil, ErrNoSuchPod
- }
-
- return nil, errors.Wrapf(err, "error parsing database row into pod")
- }
-
- pod := new(Pod)
- pod.config = new(PodConfig)
- pod.config.ID = id
- pod.config.Name = name
- pod.runtime = s.runtime
-
- // Decode labels JSON
- podLabels := make(map[string]string)
- if err := json.Unmarshal([]byte(labelsJSON), &podLabels); err != nil {
- return nil, errors.Wrapf(err, "error unmarshaling pod %s labels JSON", id)
- }
- pod.config.Labels = podLabels
-
- // Retrieve pod lock
- // Open and set the lockfile
- lockPath := filepath.Join(s.lockDir, id)
- lock, err := storage.GetLockfile(lockPath)
- if err != nil {
- return nil, errors.Wrapf(err, "error retrieving lockfile for pod %s", id)
- }
- pod.lock = lock
-
- pod.valid = true
-
- return pod, nil
-}
-
-// Internal function for adding containers
-func (s *SQLState) addContainer(ctr *Container, pod *Pod) (err error) {
- const (
- addCtr = `INSERT INTO containers VALUES (
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?,
- ?, ?, ?
- );`
- addCtrState = `INSERT INTO containerState VALUES (
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?,
- ?, ?, ?, ?, ?
- );`
- addRegistry = "INSERT INTO registry VALUES (?, ?);"
- checkCtrInPod = "SELECT 1 FROM containers WHERE Id=? AND Pod=?;"
- )
-
- if !s.valid {
- return ErrDBClosed
- }
-
- depCtrs := ctr.Dependencies()
-
- mounts, err := json.Marshal(ctr.config.Mounts)
- if err != nil {
- return errors.Wrapf(err, "error marshaling container %s mounts to JSON", ctr.ID())
- }
-
- dnsServerJSON, err := json.Marshal(ctr.config.DNSServer)
- if err != nil {
- return errors.Wrapf(err, "error marshaling container %s DNS servers to JSON", ctr.ID())
- }
-
- dnsSearchJSON, err := json.Marshal(ctr.config.DNSSearch)
- if err != nil {
- return errors.Wrapf(err, "error marshaling container %s DNS search domains to JSON", ctr.ID())
- }
-
- dnsOptionJSON, err := json.Marshal(ctr.config.DNSOption)
- if err != nil {
- return errors.Wrapf(err, "error marshaling container %s DNS options to JSON", ctr.ID())
- }
-
- hostAddJSON, err := json.Marshal(ctr.config.HostAdd)
- if err != nil {
- return errors.Wrapf(err, "error marshaling container %s hosts 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())
- }
-
- execSessionsJSON, err := json.Marshal(ctr.state.ExecSessions)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s exec sessions to JSON", ctr.ID())
- }
-
- ipsJSON, err := json.Marshal(ctr.state.IPs)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s IPs to JSON", ctr.ID())
- }
-
- routesJSON, err := json.Marshal(ctr.state.Routes)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s routes to JSON", ctr.ID())
- }
-
- bindMountsJSON, err := json.Marshal(ctr.state.BindMounts)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s bind mounts to JSON", ctr.ID())
- }
-
- netNSPath := ""
- if ctr.state.NetNS != nil {
- netNSPath = ctr.state.NetNS.Path()
- }
-
- specJSON, err := json.Marshal(ctr.config.Spec)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s spec to JSON", ctr.ID())
- }
-
- portsJSON := []byte{}
- if len(ctr.config.PortMappings) > 0 {
- portsJSON, err = json.Marshal(&ctr.config.PortMappings)
- if err != nil {
- return errors.Wrapf(err, "error marshalling container %s port mappings to JSON", ctr.ID())
- }
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
- }
- }
- }()
-
- // First check if pod exists, if one is given
- if pod != nil {
- exists, err := podExistsTx(pod.ID(), tx)
- if err != nil {
- return err
- }
-
- if !exists {
- pod.valid = false
- return errors.Wrapf(ErrNoSuchPod, "pod %s does not exist in state, cannot add container to it", pod.ID())
- }
-
- // We also need to check if our dependencies are in the pod
- for _, depID := range depCtrs {
- row := tx.QueryRow(checkCtrInPod, depID, pod.ID())
- var check int
- err := row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- return errors.Wrapf(ErrInvalidArg, "container %s depends on container %s but it is not in pod %s", ctr.ID(), depID, pod.ID())
- }
- return errors.Wrapf(err, "error querying for existence of container %s", depID)
- } else if check != 1 {
- return errors.Wrapf(ErrInternal, "check digit for checkCtrInPod query incorrect")
- }
- }
- }
-
- // Add container to registry
- if _, err := tx.Exec(addRegistry, ctr.ID(), ctr.Name()); err != nil {
- return errors.Wrapf(err, "error adding container %s to name/ID registry", ctr.ID())
- }
-
- // Add static container information
- _, err = tx.Exec(addCtr,
- ctr.ID(),
- ctr.Name(),
- stringToNullString(ctr.PodID()),
-
- ctr.config.RootfsImageID,
- ctr.config.RootfsImageName,
- boolToSQL(ctr.config.ImageVolumes),
- ctr.config.ShmDir,
- ctr.config.ShmSize,
- ctr.config.StaticDir,
- string(mounts),
- ctr.config.LogPath,
-
- boolToSQL(ctr.config.Privileged),
- ctr.config.ProcessLabel,
- ctr.config.MountLabel,
- 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),
- stringToNullString(ctr.config.CgroupNsCtr),
-
- boolToSQL(ctr.config.CreateNetNS),
- string(dnsServerJSON),
- string(dnsSearchJSON),
- string(dnsOptionJSON),
- string(hostAddJSON),
-
- boolToSQL(ctr.config.Stdin),
- string(labelsJSON),
- ctr.config.StopSignal,
- ctr.config.StopTimeout,
- timeToSQL(ctr.config.CreatedTime),
- ctr.config.CgroupParent)
- if err != nil {
- return errors.Wrapf(err, "error adding static information for container %s to database", ctr.ID())
- }
-
- // Add container state to the database
- _, err = tx.Exec(addCtrState,
- ctr.ID(),
- ctr.state.State,
- ctr.state.ConfigPath,
- ctr.state.RunDir,
- ctr.state.Mountpoint,
- timeToSQL(ctr.state.StartedTime),
- timeToSQL(ctr.state.FinishedTime),
- ctr.state.ExitCode,
- boolToSQL(ctr.state.OOMKilled),
- ctr.state.PID,
- netNSPath,
- execSessionsJSON,
- ipsJSON,
- routesJSON,
- bindMountsJSON)
- if err != nil {
- return errors.Wrapf(err, "error adding container %s state to database", ctr.ID())
- }
-
- // Save the container's runtime spec to disk
- specPath := getSpecPath(s.specsDir, ctr.ID())
- if err := ioutil.WriteFile(specPath, specJSON, 0750); err != nil {
- return errors.Wrapf(err, "error saving container %s spec JSON to disk", ctr.ID())
- }
- defer func() {
- if err != nil {
- if err2 := os.Remove(specPath); err2 != nil {
- logrus.Errorf("Error removing container %s JSON spec from state: %v", ctr.ID(), err2)
- }
- }
- }()
-
- // If the container has port mappings, save them to disk
- if len(ctr.config.PortMappings) > 0 {
- portPath := getPortsPath(s.specsDir, ctr.ID())
- if err := ioutil.WriteFile(portPath, portsJSON, 0750); err != nil {
- return errors.Wrapf(err, "error saving container %s port JSON to disk", ctr.ID())
- }
- defer func() {
- if err != nil {
- if err2 := os.Remove(portPath); err2 != nil {
- logrus.Errorf("Error removing container %s JSON ports from state: %v", ctr.ID(), err2)
- }
- }
- }()
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction to add container %s", ctr.ID())
- }
-
- return nil
-}
-
-// Internal functions for removing containers
-func (s *SQLState) removeContainer(ctr *Container, pod *Pod) (err error) {
- const (
- removeCtr = "DELETE FROM containers WHERE Id=?;"
- removeState = "DELETE FROM containerState WHERE Id=?;"
- removeRegistry = "DELETE FROM registry WHERE Id=?;"
- existsInPod = "SELECT 1 FROM containers WHERE Id=? AND Pod=?;"
- ctrExists = "SELECT 1 FROM containers WHERE Id=?;"
- )
-
- if !s.valid {
- return ErrDBClosed
- }
-
- committed := false
-
- tx, err := s.db.Begin()
- if err != nil {
- return errors.Wrapf(err, "error beginning database transaction")
- }
- defer func() {
- if err != nil && !committed {
- if err2 := tx.Rollback(); err2 != nil {
- logrus.Errorf("Error rolling back transaction to add container %s: %v", ctr.ID(), err2)
- }
- }
- }()
-
- if pod != nil {
- // Check to see if the pod exists
- exists, err := podExistsTx(pod.ID(), tx)
- if err != nil {
- return err
- }
-
- if !exists {
- pod.valid = false
- return errors.Wrapf(ErrNoSuchPod, "pod %s does not exist in state, cannot add container to it", pod.ID())
- }
-
- var check int
-
- // Check to see if the container exists
- row := tx.QueryRow(ctrExists, ctr.ID())
- err = row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- ctr.valid = false
- return errors.Wrapf(ErrNoSuchCtr, "container %s does not exist", ctr.ID())
- }
-
- return errors.Wrapf(err, "error querying database for container %s existence", ctr.ID())
- } else if check != 1 {
- return errors.Wrapf(ErrInternal, "check digit for ctr exists query incorrect")
- }
-
- // Check to see if the container is in the pod
- row = tx.QueryRow(existsInPod, ctr.ID(), pod.ID())
- err = row.Scan(&check)
- if err != nil {
- if err == sql.ErrNoRows {
- return errors.Wrapf(ErrNoSuchCtr, "container %s is not in pod %s", ctr.ID(), pod.ID())
- }
-
- return errors.Wrapf(err, "error querying database for container %s existence", ctr.ID())
- } else if check != 1 {
- return errors.Wrapf(ErrInternal, "check digit for ctr exists in pod query incorrect")
- }
- }
-
- // Check rows acted on for the first transaction, verify we actually removed something
- result, err := tx.Exec(removeCtr, ctr.ID())
- if err != nil {
- return errors.Wrapf(err, "error removing container %s from containers table", ctr.ID())
- }
- rows, err := result.RowsAffected()
- if err != nil {
- return errors.Wrapf(err, "error retrieving number of rows in transaction removing container %s", ctr.ID())
- } else if rows == 0 {
- ctr.valid = false
- return ErrNoSuchCtr
- }
-
- if _, err := tx.Exec(removeState, ctr.ID()); err != nil {
- return errors.Wrapf(err, "error removing container %s from state table", ctr.ID())
- }
-
- // Remove registry last of all
- // So we know the container did exist
- if _, err := tx.Exec(removeRegistry, ctr.ID()); err != nil {
- return errors.Wrapf(err, "error removing container %s from name/ID registry", ctr.ID())
- }
-
- committed = true
-
- if err := tx.Commit(); err != nil {
- return errors.Wrapf(err, "error committing transaction to remove container %s", ctr.ID())
- }
-
- // Remove the container's JSON from disk
- jsonPath := getSpecPath(s.specsDir, ctr.ID())
- if err := os.Remove(jsonPath); err != nil {
- return errors.Wrapf(err, "error removing JSON spec from state for container %s", ctr.ID())
- }
-
- // Remove containers ports JSON from disk
- // May not exist, so ignore os.IsNotExist
- portsPath := getPortsPath(s.specsDir, ctr.ID())
- if err := os.Remove(portsPath); err != nil {
- if !os.IsNotExist(err) {
- return errors.Wrapf(err, "error removing JSON ports from state for container %s", ctr.ID())
- }
- }
-
- ctr.valid = false
-
- return nil
-}
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 1b9a04d75..634280c99 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -24,7 +24,6 @@ const (
var (
testedStates = map[string]emptyStateFunc{
- "sql": getEmptySQLState,
"in-memory": getEmptyInMemoryState,
"boltdb": getEmptyBoltState,
}
@@ -79,35 +78,6 @@ func getEmptyInMemoryState() (s State, p string, p2 string, err error) {
return state, tmpDir, tmpDir, nil
}
-// Get an empty SQL state for use in tests
-// An empty Runtime is provided
-func getEmptySQLState() (s State, p string, p2 string, err error) {
- tmpDir, err := ioutil.TempDir("", tmpDirPrefix)
- if err != nil {
- return nil, "", "", err
- }
- defer func() {
- if err != nil {
- os.RemoveAll(tmpDir)
- }
- }()
-
- dbPath := filepath.Join(tmpDir, "db.sql")
- specsDir := filepath.Join(tmpDir, "specs")
- lockDir := filepath.Join(tmpDir, "locks")
-
- runtime := new(Runtime)
- runtime.config = new(RuntimeConfig)
- runtime.config.StorageConfig = storage.StoreOptions{}
-
- state, err := NewSQLState(dbPath, specsDir, lockDir, runtime)
- if err != nil {
- return nil, "", "", err
- }
-
- return state, tmpDir, lockDir, nil
-}
-
func runForAllStates(t *testing.T, testFunc func(*testing.T, State, string)) {
for stateName, stateFunc := range testedStates {
state, path, lockPath, err := stateFunc()