diff options
Diffstat (limited to 'libpod/sql_state_internal.go')
-rw-r--r-- | libpod/sql_state_internal.go | 1137 |
1 files changed, 0 insertions, 1137 deletions
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 -} |