github.com/containers/podman/v4@v4.9.4/libpod/sqlite_state.go (about)

     1  //go:build !remote
     2  // +build !remote
     3  
     4  package libpod
     5  
     6  import (
     7  	"database/sql"
     8  	"errors"
     9  	"fmt"
    10  	"io/fs"
    11  	"os"
    12  	"path/filepath"
    13  	goruntime "runtime"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/containers/common/libnetwork/types"
    18  	"github.com/containers/podman/v4/libpod/define"
    19  	"github.com/containers/podman/v4/pkg/rootless"
    20  	"github.com/containers/storage"
    21  	"github.com/sirupsen/logrus"
    22  
    23  	// SQLite backend for database/sql
    24  	_ "github.com/mattn/go-sqlite3"
    25  )
    26  
    27  const schemaVersion = 1
    28  
    29  // SQLiteState is a state implementation backed by a SQLite database
    30  type SQLiteState struct {
    31  	valid   bool
    32  	conn    *sql.DB
    33  	runtime *Runtime
    34  }
    35  
    36  const (
    37  	// Deal with timezone automatically.
    38  	sqliteOptionLocation = "_loc=auto"
    39  	// Force an fsync after each transaction (https://www.sqlite.org/pragma.html#pragma_synchronous).
    40  	sqliteOptionSynchronous = "&_sync=FULL"
    41  	// Allow foreign keys (https://www.sqlite.org/pragma.html#pragma_foreign_keys).
    42  	sqliteOptionForeignKeys = "&_foreign_keys=1"
    43  	// Make sure that transactions happen exclusively.
    44  	sqliteOptionTXLock = "&_txlock=exclusive"
    45  	// Make sure busy timeout is set to high value to keep retying when the db is locked.
    46  	// Timeout is in ms, so set it to 100s to have enough time to retry the operations.
    47  	sqliteOptionBusyTimeout = "&_busy_timeout=100000"
    48  
    49  	// Assembled sqlite options used when opening the database.
    50  	sqliteOptions = "db.sql?" +
    51  		sqliteOptionLocation +
    52  		sqliteOptionSynchronous +
    53  		sqliteOptionForeignKeys +
    54  		sqliteOptionTXLock +
    55  		sqliteOptionBusyTimeout
    56  )
    57  
    58  // NewSqliteState creates a new SQLite-backed state database.
    59  func NewSqliteState(runtime *Runtime) (_ State, defErr error) {
    60  	logrus.Info("Using sqlite as database backend")
    61  	state := new(SQLiteState)
    62  
    63  	basePath := runtime.storageConfig.GraphRoot
    64  	if runtime.storageConfig.TransientStore {
    65  		basePath = runtime.storageConfig.RunRoot
    66  	} else if !runtime.storageSet.StaticDirSet {
    67  		basePath = runtime.config.Engine.StaticDir
    68  	}
    69  
    70  	// c/storage is set up *after* the DB - so even though we use the c/s
    71  	// root (or, for transient, runroot) dir, we need to make the dir
    72  	// ourselves.
    73  	if err := os.MkdirAll(basePath, 0700); err != nil {
    74  		return nil, fmt.Errorf("creating root directory: %w", err)
    75  	}
    76  
    77  	conn, err := sql.Open("sqlite3", filepath.Join(basePath, sqliteOptions))
    78  	if err != nil {
    79  		return nil, fmt.Errorf("initializing sqlite database: %w", err)
    80  	}
    81  	defer func() {
    82  		if defErr != nil {
    83  			if err := conn.Close(); err != nil {
    84  				logrus.Errorf("Error closing SQLite DB connection: %v", err)
    85  			}
    86  		}
    87  	}()
    88  
    89  	if err := initSQLiteDB(conn); err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	state.conn = conn
    94  	state.valid = true
    95  	state.runtime = runtime
    96  
    97  	return state, nil
    98  }
    99  
   100  // Close closes the state and prevents further use
   101  func (s *SQLiteState) Close() error {
   102  	if err := s.conn.Close(); err != nil {
   103  		return err
   104  	}
   105  
   106  	s.valid = false
   107  	return nil
   108  }
   109  
   110  // Refresh clears container and pod states after a reboot
   111  func (s *SQLiteState) Refresh() (defErr error) {
   112  	if !s.valid {
   113  		return define.ErrDBClosed
   114  	}
   115  
   116  	// Retrieve all containers, pods, and volumes.
   117  	// Maps are indexed by ID (or volume name) so we know which goes where,
   118  	// and store the marshalled state JSON
   119  	ctrStates := make(map[string]string)
   120  	podStates := make(map[string]string)
   121  	volumeStates := make(map[string]string)
   122  
   123  	ctrRows, err := s.conn.Query("SELECT ID, JSON FROM ContainerState;")
   124  	if err != nil {
   125  		return fmt.Errorf("querying for container states: %w", err)
   126  	}
   127  	defer ctrRows.Close()
   128  
   129  	for ctrRows.Next() {
   130  		var (
   131  			id, stateJSON string
   132  		)
   133  		if err := ctrRows.Scan(&id, &stateJSON); err != nil {
   134  			return fmt.Errorf("scanning container state row: %w", err)
   135  		}
   136  
   137  		ctrState := new(ContainerState)
   138  
   139  		if err := json.Unmarshal([]byte(stateJSON), ctrState); err != nil {
   140  			return fmt.Errorf("unmarshalling container state json: %w", err)
   141  		}
   142  
   143  		// Refresh the state
   144  		resetContainerState(ctrState)
   145  
   146  		newJSON, err := json.Marshal(ctrState)
   147  		if err != nil {
   148  			return fmt.Errorf("marshalling container state json: %w", err)
   149  		}
   150  
   151  		ctrStates[id] = string(newJSON)
   152  	}
   153  	if err := ctrRows.Err(); err != nil {
   154  		return err
   155  	}
   156  
   157  	podRows, err := s.conn.Query("SELECT ID, JSON FROM PodState;")
   158  	if err != nil {
   159  		return fmt.Errorf("querying for pod states: %w", err)
   160  	}
   161  	defer podRows.Close()
   162  
   163  	for podRows.Next() {
   164  		var (
   165  			id, stateJSON string
   166  		)
   167  		if err := podRows.Scan(&id, &stateJSON); err != nil {
   168  			return fmt.Errorf("scanning pod state row: %w", err)
   169  		}
   170  
   171  		podState := new(podState)
   172  
   173  		if err := json.Unmarshal([]byte(stateJSON), podState); err != nil {
   174  			return fmt.Errorf("unmarshalling pod state json: %w", err)
   175  		}
   176  
   177  		// Refresh the state
   178  		resetPodState(podState)
   179  
   180  		newJSON, err := json.Marshal(podState)
   181  		if err != nil {
   182  			return fmt.Errorf("marshalling pod state json: %w", err)
   183  		}
   184  
   185  		podStates[id] = string(newJSON)
   186  	}
   187  	if err := podRows.Err(); err != nil {
   188  		return err
   189  	}
   190  
   191  	volRows, err := s.conn.Query("SELECT Name, JSON FROM VolumeState;")
   192  	if err != nil {
   193  		return fmt.Errorf("querying for volume states: %w", err)
   194  	}
   195  	defer volRows.Close()
   196  
   197  	for volRows.Next() {
   198  		var (
   199  			name, stateJSON string
   200  		)
   201  
   202  		if err := volRows.Scan(&name, &stateJSON); err != nil {
   203  			return fmt.Errorf("scanning volume state row: %w", err)
   204  		}
   205  
   206  		volState := new(VolumeState)
   207  
   208  		if err := json.Unmarshal([]byte(stateJSON), volState); err != nil {
   209  			return fmt.Errorf("unmarshalling volume state json: %w", err)
   210  		}
   211  
   212  		// Refresh the state
   213  		resetVolumeState(volState)
   214  
   215  		newJSON, err := json.Marshal(volState)
   216  		if err != nil {
   217  			return fmt.Errorf("marshalling volume state json: %w", err)
   218  		}
   219  
   220  		volumeStates[name] = string(newJSON)
   221  	}
   222  	if err := volRows.Err(); err != nil {
   223  		return err
   224  	}
   225  
   226  	// Write updated states back to DB, and perform additional maintenance
   227  	// (Remove exit codes and exec sessions)
   228  
   229  	tx, err := s.conn.Begin()
   230  	if err != nil {
   231  		return fmt.Errorf("beginning refresh transaction: %w", err)
   232  	}
   233  	defer func() {
   234  		if defErr != nil {
   235  			if err := tx.Rollback(); err != nil {
   236  				logrus.Errorf("Rolling back transaction to refresh database state: %v", err)
   237  			}
   238  		}
   239  	}()
   240  
   241  	for id, json := range ctrStates {
   242  		if _, err := tx.Exec("UPDATE ContainerState SET JSON=? WHERE ID=?;", json, id); err != nil {
   243  			return fmt.Errorf("updating container state: %w", err)
   244  		}
   245  	}
   246  	for id, json := range podStates {
   247  		if _, err := tx.Exec("UPDATE PodState SET JSON=? WHERE ID=?;", json, id); err != nil {
   248  			return fmt.Errorf("updating pod state: %w", err)
   249  		}
   250  	}
   251  	for name, json := range volumeStates {
   252  		if _, err := tx.Exec("UPDATE VolumeState SET JSON=? WHERE Name=?;", json, name); err != nil {
   253  			return fmt.Errorf("updating volume state: %w", err)
   254  		}
   255  	}
   256  
   257  	if _, err := tx.Exec("DELETE FROM ContainerExitCode;"); err != nil {
   258  		return fmt.Errorf("removing container exit codes: %w", err)
   259  	}
   260  
   261  	if _, err := tx.Exec("DELETE FROM ContainerExecSession;"); err != nil {
   262  		return fmt.Errorf("removing container exec sessions: %w", err)
   263  	}
   264  
   265  	if err := tx.Commit(); err != nil {
   266  		return fmt.Errorf("committing transaction: %w", err)
   267  	}
   268  
   269  	return nil
   270  }
   271  
   272  // GetDBConfig retrieves runtime configuration fields that were created when
   273  // the database was first initialized
   274  func (s *SQLiteState) GetDBConfig() (*DBConfig, error) {
   275  	if !s.valid {
   276  		return nil, define.ErrDBClosed
   277  	}
   278  
   279  	cfg := new(DBConfig)
   280  	var staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumeDir string
   281  
   282  	row := s.conn.QueryRow("SELECT StaticDir, TmpDir, GraphRoot, RunRoot, GraphDriver, VolumeDir FROM DBConfig;")
   283  
   284  	if err := row.Scan(&staticDir, &tmpDir, &graphRoot, &runRoot, &graphDriver, &volumeDir); err != nil {
   285  		if errors.Is(err, sql.ErrNoRows) {
   286  			return cfg, nil
   287  		}
   288  		return nil, fmt.Errorf("retrieving DB config: %w", err)
   289  	}
   290  
   291  	cfg.LibpodRoot = staticDir
   292  	cfg.LibpodTmp = tmpDir
   293  	cfg.StorageRoot = graphRoot
   294  	cfg.StorageTmp = runRoot
   295  	cfg.GraphDriver = graphDriver
   296  	cfg.VolumePath = volumeDir
   297  
   298  	return cfg, nil
   299  }
   300  
   301  // ValidateDBConfig validates paths in the given runtime against the database
   302  func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
   303  	if !s.valid {
   304  		return define.ErrDBClosed
   305  	}
   306  
   307  	storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID())
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	const createRow = `
   313          INSERT INTO DBconfig VALUES (
   314                  ?, ?, ?,
   315                  ?, ?, ?,
   316                  ?, ?, ?
   317          );`
   318  
   319  	var (
   320  		dbOS, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
   321  		runtimeOS                                                            = goruntime.GOOS
   322  		runtimeStaticDir                                                     = filepath.Clean(s.runtime.config.Engine.StaticDir)
   323  		runtimeTmpDir                                                        = filepath.Clean(s.runtime.config.Engine.TmpDir)
   324  		runtimeGraphRoot                                                     = filepath.Clean(s.runtime.StorageConfig().GraphRoot)
   325  		runtimeRunRoot                                                       = filepath.Clean(s.runtime.StorageConfig().RunRoot)
   326  		runtimeGraphDriver                                                   = s.runtime.StorageConfig().GraphDriverName
   327  		runtimeVolumePath                                                    = filepath.Clean(s.runtime.config.Engine.VolumePath)
   328  	)
   329  
   330  	// Some fields may be empty, indicating they are set to the default.
   331  	// If so, grab the default from c/storage for them.
   332  	if runtimeGraphRoot == "" {
   333  		runtimeGraphRoot = storeOpts.GraphRoot
   334  	}
   335  	if runtimeRunRoot == "" {
   336  		runtimeRunRoot = storeOpts.RunRoot
   337  	}
   338  	if runtimeGraphDriver == "" {
   339  		runtimeGraphDriver = storeOpts.GraphDriverName
   340  	}
   341  
   342  	// We have to do this in a transaction to ensure mutual exclusion.
   343  	// Otherwise we have a race - multiple processes can be checking the
   344  	// row's existence simultaneously, both try to create it, second one to
   345  	// get the transaction lock gets an error.
   346  	// TODO: The transaction isn't strictly necessary, and there's a (small)
   347  	// chance it's a perf hit. If it is, we can move it entirely within the
   348  	// `errors.Is()` block below, with extra validation to ensure the row
   349  	// still does not exist (and, if it does, to retry this function).
   350  	tx, err := s.conn.Begin()
   351  	if err != nil {
   352  		return fmt.Errorf("beginning database validation transaction: %w", err)
   353  	}
   354  	defer func() {
   355  		if defErr != nil {
   356  			if err := tx.Rollback(); err != nil {
   357  				logrus.Errorf("Rolling back transaction to validate database: %v", err)
   358  			}
   359  		}
   360  	}()
   361  
   362  	row := tx.QueryRow("SELECT Os, StaticDir, TmpDir, GraphRoot, RunRoot, GraphDriver, VolumeDir FROM DBConfig;")
   363  
   364  	if err := row.Scan(&dbOS, &staticDir, &tmpDir, &graphRoot, &runRoot, &graphDriver, &volumePath); err != nil {
   365  		if errors.Is(err, sql.ErrNoRows) {
   366  			if _, err := tx.Exec(createRow, 1, schemaVersion, runtimeOS,
   367  				runtimeStaticDir, runtimeTmpDir, runtimeGraphRoot,
   368  				runtimeRunRoot, runtimeGraphDriver, runtimeVolumePath); err != nil {
   369  				return fmt.Errorf("adding DB config row: %w", err)
   370  			}
   371  
   372  			if err := tx.Commit(); err != nil {
   373  				return fmt.Errorf("committing write of database validation row: %w", err)
   374  			}
   375  
   376  			return nil
   377  		}
   378  
   379  		return fmt.Errorf("retrieving DB config: %w", err)
   380  	}
   381  
   382  	checkField := func(fieldName, dbVal, ourVal string, isPath bool) error {
   383  		if isPath {
   384  			// Evaluate symlinks. Ignore ENOENT. No guarantee all
   385  			// directories exist this early in Libpod init.
   386  			if dbVal != "" {
   387  				dbValClean, err := filepath.EvalSymlinks(dbVal)
   388  				if err != nil && !errors.Is(err, fs.ErrNotExist) {
   389  					return fmt.Errorf("cannot evaluate symlinks on DB %s path %q: %w", fieldName, dbVal, err)
   390  				}
   391  				dbVal = dbValClean
   392  			}
   393  			if ourVal != "" {
   394  				ourValClean, err := filepath.EvalSymlinks(ourVal)
   395  				if err != nil && !errors.Is(err, fs.ErrNotExist) {
   396  					return fmt.Errorf("cannot evaluate symlinks on our %s path %q: %w", fieldName, ourVal, err)
   397  				}
   398  				ourVal = ourValClean
   399  			}
   400  		}
   401  
   402  		if dbVal != ourVal {
   403  			return fmt.Errorf("database %s %q does not match our %s %q: %w", fieldName, dbVal, fieldName, ourVal, define.ErrDBBadConfig)
   404  		}
   405  
   406  		return nil
   407  	}
   408  
   409  	if err := checkField("os", dbOS, runtimeOS, false); err != nil {
   410  		return err
   411  	}
   412  	if err := checkField("static dir", staticDir, runtimeStaticDir, true); err != nil {
   413  		return err
   414  	}
   415  	if err := checkField("tmp dir", tmpDir, runtimeTmpDir, true); err != nil {
   416  		return err
   417  	}
   418  	if err := checkField("graph root", graphRoot, runtimeGraphRoot, true); err != nil {
   419  		return err
   420  	}
   421  	if err := checkField("run root", runRoot, runtimeRunRoot, true); err != nil {
   422  		return err
   423  	}
   424  	if err := checkField("graph driver", graphDriver, runtimeGraphDriver, false); err != nil {
   425  		return err
   426  	}
   427  	if err := checkField("volume path", volumePath, runtimeVolumePath, true); err != nil {
   428  		return err
   429  	}
   430  
   431  	if err := tx.Commit(); err != nil {
   432  		return fmt.Errorf("committing database validation row: %w", err)
   433  	}
   434  	// Do not return any error after the commit call because the defer will
   435  	// try to roll back the transaction which results in an logged error.
   436  
   437  	return nil
   438  }
   439  
   440  // GetContainerName returns the name of the container associated with a given
   441  // ID. Returns ErrNoSuchCtr if the ID does not exist.
   442  func (s *SQLiteState) GetContainerName(id string) (string, error) {
   443  	if id == "" {
   444  		return "", define.ErrEmptyID
   445  	}
   446  
   447  	if !s.valid {
   448  		return "", define.ErrDBClosed
   449  	}
   450  
   451  	var name string
   452  
   453  	row := s.conn.QueryRow("SELECT Name FROM ContainerConfig WHERE ID=?;", id)
   454  	if err := row.Scan(&name); err != nil {
   455  		if errors.Is(err, sql.ErrNoRows) {
   456  			return "", define.ErrNoSuchCtr
   457  		}
   458  
   459  		return "", fmt.Errorf("looking up container %s name: %w", id, err)
   460  	}
   461  
   462  	return name, nil
   463  }
   464  
   465  // GetPodName returns the name of the pod associated with a given ID.
   466  // Returns ErrNoSuchPod if the ID does not exist.
   467  func (s *SQLiteState) GetPodName(id string) (string, error) {
   468  	if id == "" {
   469  		return "", define.ErrEmptyID
   470  	}
   471  
   472  	if !s.valid {
   473  		return "", define.ErrDBClosed
   474  	}
   475  
   476  	var name string
   477  
   478  	row := s.conn.QueryRow("SELECT Name FROM PodConfig WHERE ID=?;", id)
   479  	if err := row.Scan(&name); err != nil {
   480  		if errors.Is(err, sql.ErrNoRows) {
   481  			return "", define.ErrNoSuchPod
   482  		}
   483  
   484  		return "", fmt.Errorf("looking up pod %s name: %w", id, err)
   485  	}
   486  
   487  	return name, nil
   488  }
   489  
   490  // Container retrieves a single container from the state by its full ID
   491  func (s *SQLiteState) Container(id string) (*Container, error) {
   492  	if id == "" {
   493  		return nil, define.ErrEmptyID
   494  	}
   495  
   496  	if !s.valid {
   497  		return nil, define.ErrDBClosed
   498  	}
   499  
   500  	ctrConfig, err := s.getCtrConfig(id)
   501  	if err != nil {
   502  		return nil, err
   503  	}
   504  
   505  	ctr := new(Container)
   506  	ctr.config = ctrConfig
   507  	ctr.state = new(ContainerState)
   508  	ctr.runtime = s.runtime
   509  
   510  	if err := finalizeCtrSqlite(ctr); err != nil {
   511  		return nil, err
   512  	}
   513  
   514  	return ctr, nil
   515  }
   516  
   517  // LookupContainerID retrieves a container ID from the state by full or unique
   518  // partial ID or name
   519  func (s *SQLiteState) LookupContainerID(idOrName string) (string, error) {
   520  	if idOrName == "" {
   521  		return "", define.ErrEmptyID
   522  	}
   523  
   524  	if !s.valid {
   525  		return "", define.ErrDBClosed
   526  	}
   527  
   528  	rows, err := s.conn.Query("SELECT ID, Name FROM ContainerConfig WHERE ContainerConfig.Name=? OR (ContainerConfig.ID LIKE ?);", idOrName, idOrName+"%")
   529  	if err != nil {
   530  		return "", fmt.Errorf("looking up container %q in database: %w", idOrName, err)
   531  	}
   532  	defer rows.Close()
   533  
   534  	var (
   535  		id, name string
   536  		resCount uint
   537  	)
   538  	for rows.Next() {
   539  		if err := rows.Scan(&id, &name); err != nil {
   540  			return "", fmt.Errorf("retrieving container %q ID from database: %w", idOrName, err)
   541  		}
   542  		if name == idOrName {
   543  			return id, nil
   544  		}
   545  		resCount++
   546  	}
   547  	if err := rows.Err(); err != nil {
   548  		return "", err
   549  	}
   550  	if resCount == 0 {
   551  		return "", define.ErrNoSuchCtr
   552  	} else if resCount > 1 {
   553  		return "", fmt.Errorf("more than one result for container %q: %w", idOrName, define.ErrCtrExists)
   554  	}
   555  
   556  	return id, nil
   557  }
   558  
   559  // LookupContainer retrieves a container from the state by full or unique
   560  // partial ID or name
   561  func (s *SQLiteState) LookupContainer(idOrName string) (*Container, error) {
   562  	if idOrName == "" {
   563  		return nil, define.ErrEmptyID
   564  	}
   565  
   566  	if !s.valid {
   567  		return nil, define.ErrDBClosed
   568  	}
   569  
   570  	rows, err := s.conn.Query("SELECT JSON, Name FROM ContainerConfig WHERE ContainerConfig.Name=? OR (ContainerConfig.ID LIKE ?);", idOrName, idOrName+"%")
   571  	if err != nil {
   572  		return nil, fmt.Errorf("looking up container %q in database: %w", idOrName, err)
   573  	}
   574  	defer rows.Close()
   575  
   576  	var (
   577  		rawJSON, name string
   578  		exactName     bool
   579  		resCount      uint
   580  	)
   581  	for rows.Next() {
   582  		if err := rows.Scan(&rawJSON, &name); err != nil {
   583  			return nil, fmt.Errorf("retrieving container %q ID from database: %w", idOrName, err)
   584  		}
   585  		if name == idOrName {
   586  			exactName = true
   587  			break
   588  		}
   589  		resCount++
   590  	}
   591  	if err := rows.Err(); err != nil {
   592  		return nil, err
   593  	}
   594  	if !exactName {
   595  		if resCount == 0 {
   596  			return nil, fmt.Errorf("no container with name or ID %q found: %w", idOrName, define.ErrNoSuchCtr)
   597  		} else if resCount > 1 {
   598  			return nil, fmt.Errorf("more than one result for container %q: %w", idOrName, define.ErrCtrExists)
   599  		}
   600  	}
   601  
   602  	ctr := new(Container)
   603  	ctr.config = new(ContainerConfig)
   604  	ctr.state = new(ContainerState)
   605  	ctr.runtime = s.runtime
   606  
   607  	if err := json.Unmarshal([]byte(rawJSON), ctr.config); err != nil {
   608  		return nil, fmt.Errorf("unmarshalling container config JSON: %w", err)
   609  	}
   610  
   611  	if err := finalizeCtrSqlite(ctr); err != nil {
   612  		return nil, err
   613  	}
   614  
   615  	return ctr, nil
   616  }
   617  
   618  // HasContainer checks if a container is present in the state
   619  func (s *SQLiteState) HasContainer(id string) (bool, error) {
   620  	if id == "" {
   621  		return false, define.ErrEmptyID
   622  	}
   623  
   624  	if !s.valid {
   625  		return false, define.ErrDBClosed
   626  	}
   627  
   628  	row := s.conn.QueryRow("SELECT 1 FROM ContainerConfig WHERE ID=?;", id)
   629  
   630  	var check int
   631  	if err := row.Scan(&check); err != nil {
   632  		if errors.Is(err, sql.ErrNoRows) {
   633  			return false, nil
   634  		}
   635  		return false, fmt.Errorf("looking up container %s in database: %w", id, err)
   636  	} else if check != 1 {
   637  		return false, fmt.Errorf("check digit for container %s lookup incorrect: %w", id, define.ErrInternal)
   638  	}
   639  
   640  	return true, nil
   641  }
   642  
   643  // AddContainer adds a container to the state
   644  // The container being added cannot belong to a pod
   645  func (s *SQLiteState) AddContainer(ctr *Container) error {
   646  	if !s.valid {
   647  		return define.ErrDBClosed
   648  	}
   649  
   650  	if !ctr.valid {
   651  		return define.ErrCtrRemoved
   652  	}
   653  
   654  	if ctr.config.Pod != "" {
   655  		return fmt.Errorf("cannot add a container that belongs to a pod with AddContainer - use AddContainerToPod: %w", define.ErrInvalidArg)
   656  	}
   657  
   658  	return s.addContainer(ctr)
   659  }
   660  
   661  // RemoveContainer removes a container from the state
   662  // Only removes containers not in pods - for containers that are a member of a
   663  // pod, use RemoveContainerFromPod
   664  func (s *SQLiteState) RemoveContainer(ctr *Container) error {
   665  	if !s.valid {
   666  		return define.ErrDBClosed
   667  	}
   668  
   669  	if ctr.config.Pod != "" {
   670  		return fmt.Errorf("container %s is part of a pod, use RemoveContainerFromPod instead: %w", ctr.ID(), define.ErrPodExists)
   671  	}
   672  
   673  	return s.removeContainer(ctr)
   674  }
   675  
   676  // UpdateContainer updates a container's state from the database
   677  func (s *SQLiteState) UpdateContainer(ctr *Container) error {
   678  	if !s.valid {
   679  		return define.ErrDBClosed
   680  	}
   681  
   682  	if !ctr.valid {
   683  		return define.ErrCtrRemoved
   684  	}
   685  
   686  	row := s.conn.QueryRow("SELECT JSON FROM ContainerState WHERE ID=?;", ctr.ID())
   687  
   688  	var rawJSON string
   689  	if err := row.Scan(&rawJSON); err != nil {
   690  		if errors.Is(err, sql.ErrNoRows) {
   691  			// Container was removed
   692  			ctr.valid = false
   693  			return fmt.Errorf("no container with ID %s found in database: %w", ctr.ID(), define.ErrNoSuchCtr)
   694  		}
   695  	}
   696  
   697  	newState := new(ContainerState)
   698  	if err := json.Unmarshal([]byte(rawJSON), newState); err != nil {
   699  		return fmt.Errorf("unmarshalling container %s state JSON: %w", ctr.ID(), err)
   700  	}
   701  
   702  	ctr.state = newState
   703  
   704  	return nil
   705  }
   706  
   707  // SaveContainer saves a container's current state in the database
   708  func (s *SQLiteState) SaveContainer(ctr *Container) (defErr error) {
   709  	if !s.valid {
   710  		return define.ErrDBClosed
   711  	}
   712  
   713  	if !ctr.valid {
   714  		return define.ErrCtrRemoved
   715  	}
   716  
   717  	stateJSON, err := json.Marshal(ctr.state)
   718  	if err != nil {
   719  		return fmt.Errorf("marshalling container %s state JSON: %w", ctr.ID(), err)
   720  	}
   721  
   722  	tx, err := s.conn.Begin()
   723  	if err != nil {
   724  		return fmt.Errorf("beginning container %s save transaction: %w", ctr.ID(), err)
   725  	}
   726  	defer func() {
   727  		if defErr != nil {
   728  			if err := tx.Rollback(); err != nil {
   729  				logrus.Errorf("Rolling back transaction to save container %s state: %v", ctr.ID(), err)
   730  			}
   731  		}
   732  	}()
   733  
   734  	result, err := tx.Exec("UPDATE ContainerState SET JSON=? WHERE ID=?;", stateJSON, ctr.ID())
   735  	if err != nil {
   736  		return fmt.Errorf("writing container %s state: %w", ctr.ID(), err)
   737  	}
   738  	rows, err := result.RowsAffected()
   739  	if err != nil {
   740  		return fmt.Errorf("retrieving container %s save rows affected: %w", ctr.ID(), err)
   741  	}
   742  	if rows == 0 {
   743  		ctr.valid = false
   744  		return define.ErrNoSuchCtr
   745  	}
   746  
   747  	if err := tx.Commit(); err != nil {
   748  		return fmt.Errorf("committing container %s state: %w", ctr.ID(), err)
   749  	}
   750  
   751  	return nil
   752  }
   753  
   754  // ContainerInUse checks if other containers depend on the given container
   755  // It returns a slice of the IDs of the containers depending on the given
   756  // container. If the slice is empty, no containers depend on the given container
   757  func (s *SQLiteState) ContainerInUse(ctr *Container) ([]string, error) {
   758  	if !s.valid {
   759  		return nil, define.ErrDBClosed
   760  	}
   761  
   762  	if !ctr.valid {
   763  		return nil, define.ErrCtrRemoved
   764  	}
   765  
   766  	rows, err := s.conn.Query("SELECT ID FROM ContainerDependency WHERE DependencyID=?;", ctr.ID())
   767  	if err != nil {
   768  		return nil, fmt.Errorf("retrieving containers that depend on container %s: %w", ctr.ID(), err)
   769  	}
   770  	defer rows.Close()
   771  
   772  	deps := []string{}
   773  	for rows.Next() {
   774  		var dep string
   775  		if err := rows.Scan(&dep); err != nil {
   776  			return nil, fmt.Errorf("reading containers that depend on %s: %w", ctr.ID(), err)
   777  		}
   778  		deps = append(deps, dep)
   779  	}
   780  	if err := rows.Err(); err != nil {
   781  		return nil, err
   782  	}
   783  
   784  	return deps, nil
   785  }
   786  
   787  // AllContainers retrieves all the containers in the database
   788  // If `loadState` is set, the containers' state will be loaded as well.
   789  func (s *SQLiteState) AllContainers(loadState bool) ([]*Container, error) {
   790  	if !s.valid {
   791  		return nil, define.ErrDBClosed
   792  	}
   793  
   794  	ctrs := []*Container{}
   795  
   796  	if loadState {
   797  		rows, err := s.conn.Query("SELECT ContainerConfig.JSON, ContainerState.JSON AS StateJSON FROM ContainerConfig INNER JOIN ContainerState ON ContainerConfig.ID = ContainerState.ID;")
   798  		if err != nil {
   799  			return nil, fmt.Errorf("retrieving all containers from database: %w", err)
   800  		}
   801  		defer rows.Close()
   802  
   803  		for rows.Next() {
   804  			var configJSON, stateJSON string
   805  			if err := rows.Scan(&configJSON, &stateJSON); err != nil {
   806  				return nil, fmt.Errorf("scanning container from database: %w", err)
   807  			}
   808  
   809  			ctr := new(Container)
   810  			ctr.config = new(ContainerConfig)
   811  			ctr.state = new(ContainerState)
   812  			ctr.runtime = s.runtime
   813  
   814  			if err := json.Unmarshal([]byte(configJSON), ctr.config); err != nil {
   815  				return nil, fmt.Errorf("unmarshalling container config: %w", err)
   816  			}
   817  			if err := json.Unmarshal([]byte(stateJSON), ctr.state); err != nil {
   818  				return nil, fmt.Errorf("unmarshalling container %s state: %w", ctr.ID(), err)
   819  			}
   820  
   821  			ctrs = append(ctrs, ctr)
   822  		}
   823  		if err := rows.Err(); err != nil {
   824  			return nil, err
   825  		}
   826  	} else {
   827  		rows, err := s.conn.Query("SELECT JSON FROM ContainerConfig;")
   828  		if err != nil {
   829  			return nil, fmt.Errorf("retrieving all containers from database: %w", err)
   830  		}
   831  		defer rows.Close()
   832  
   833  		for rows.Next() {
   834  			var rawJSON string
   835  			if err := rows.Scan(&rawJSON); err != nil {
   836  				return nil, fmt.Errorf("scanning container from database: %w", err)
   837  			}
   838  
   839  			ctr := new(Container)
   840  			ctr.config = new(ContainerConfig)
   841  			ctr.state = new(ContainerState)
   842  			ctr.runtime = s.runtime
   843  
   844  			if err := json.Unmarshal([]byte(rawJSON), ctr.config); err != nil {
   845  				return nil, fmt.Errorf("unmarshalling container config: %w", err)
   846  			}
   847  
   848  			ctrs = append(ctrs, ctr)
   849  		}
   850  		if err := rows.Err(); err != nil {
   851  			return nil, err
   852  		}
   853  	}
   854  
   855  	for _, ctr := range ctrs {
   856  		if err := finalizeCtrSqlite(ctr); err != nil {
   857  			return nil, err
   858  		}
   859  	}
   860  
   861  	return ctrs, nil
   862  }
   863  
   864  // GetNetworks returns the networks this container is a part of.
   865  func (s *SQLiteState) GetNetworks(ctr *Container) (map[string]types.PerNetworkOptions, error) {
   866  	if !s.valid {
   867  		return nil, define.ErrDBClosed
   868  	}
   869  
   870  	if !ctr.valid {
   871  		return nil, define.ErrCtrRemoved
   872  	}
   873  
   874  	// if the network mode is not bridge return no networks
   875  	if !ctr.config.NetMode.IsBridge() {
   876  		return nil, nil
   877  	}
   878  
   879  	cfg, err := s.getCtrConfig(ctr.ID())
   880  	if err != nil {
   881  		if errors.Is(err, define.ErrNoSuchCtr) {
   882  			ctr.valid = false
   883  		}
   884  		return nil, err
   885  	}
   886  
   887  	return cfg.Networks, nil
   888  }
   889  
   890  // NetworkConnect adds the given container to the given network. If aliases are
   891  // specified, those will be added to the given network.
   892  func (s *SQLiteState) NetworkConnect(ctr *Container, network string, opts types.PerNetworkOptions) error {
   893  	return s.networkModify(ctr, network, opts, true, false)
   894  }
   895  
   896  // NetworkModify will allow you to set new options on an existing connected network
   897  func (s *SQLiteState) NetworkModify(ctr *Container, network string, opts types.PerNetworkOptions) error {
   898  	return s.networkModify(ctr, network, opts, false, false)
   899  }
   900  
   901  // NetworkDisconnect disconnects the container from the given network, also
   902  // removing any aliases in the network.
   903  func (s *SQLiteState) NetworkDisconnect(ctr *Container, network string) error {
   904  	return s.networkModify(ctr, network, types.PerNetworkOptions{}, false, true)
   905  }
   906  
   907  // GetContainerConfig returns a container config from the database by full ID
   908  func (s *SQLiteState) GetContainerConfig(id string) (*ContainerConfig, error) {
   909  	if len(id) == 0 {
   910  		return nil, define.ErrEmptyID
   911  	}
   912  
   913  	if !s.valid {
   914  		return nil, define.ErrDBClosed
   915  	}
   916  
   917  	return s.getCtrConfig(id)
   918  }
   919  
   920  // AddContainerExitCode adds the exit code for the specified container to the database.
   921  func (s *SQLiteState) AddContainerExitCode(id string, exitCode int32) (defErr error) {
   922  	if len(id) == 0 {
   923  		return define.ErrEmptyID
   924  	}
   925  
   926  	if !s.valid {
   927  		return define.ErrDBClosed
   928  	}
   929  
   930  	tx, err := s.conn.Begin()
   931  	if err != nil {
   932  		return fmt.Errorf("beginning transaction to add exit code: %w", err)
   933  	}
   934  	defer func() {
   935  		if defErr != nil {
   936  			if err := tx.Rollback(); err != nil {
   937  				logrus.Errorf("Rolling back transaction to add exit code: %v", err)
   938  			}
   939  		}
   940  	}()
   941  
   942  	if _, err := tx.Exec("INSERT OR REPLACE INTO ContainerExitCode VALUES (?, ?, ?);", id, time.Now().Unix(), exitCode); err != nil {
   943  		return fmt.Errorf("adding container %s exit code %d: %w", id, exitCode, err)
   944  	}
   945  
   946  	if err := tx.Commit(); err != nil {
   947  		return fmt.Errorf("committing transaction to add exit code: %w", err)
   948  	}
   949  
   950  	return nil
   951  }
   952  
   953  // GetContainerExitCode returns the exit code for the specified container.
   954  func (s *SQLiteState) GetContainerExitCode(id string) (int32, error) {
   955  	if len(id) == 0 {
   956  		return -1, define.ErrEmptyID
   957  	}
   958  
   959  	if !s.valid {
   960  		return -1, define.ErrDBClosed
   961  	}
   962  
   963  	row := s.conn.QueryRow("SELECT ExitCode FROM ContainerExitCode WHERE ID=?;", id)
   964  
   965  	var exitCode int32
   966  	if err := row.Scan(&exitCode); err != nil {
   967  		if errors.Is(err, sql.ErrNoRows) {
   968  			return -1, fmt.Errorf("getting exit code of container %s from DB: %w", id, define.ErrNoSuchExitCode)
   969  		}
   970  		return -1, fmt.Errorf("scanning exit code of container %s: %w", id, err)
   971  	}
   972  
   973  	return exitCode, nil
   974  }
   975  
   976  // GetContainerExitCodeTimeStamp returns the time stamp when the exit code of
   977  // the specified container was added to the database.
   978  func (s *SQLiteState) GetContainerExitCodeTimeStamp(id string) (*time.Time, error) {
   979  	if len(id) == 0 {
   980  		return nil, define.ErrEmptyID
   981  	}
   982  
   983  	if !s.valid {
   984  		return nil, define.ErrDBClosed
   985  	}
   986  
   987  	row := s.conn.QueryRow("SELECT Timestamp FROM ContainerExitCode WHERE ID=?;", id)
   988  
   989  	var timestamp int64
   990  	if err := row.Scan(&timestamp); err != nil {
   991  		if errors.Is(err, sql.ErrNoRows) {
   992  			return nil, fmt.Errorf("getting timestamp for exit code of container %s from DB: %w", id, define.ErrNoSuchExitCode)
   993  		}
   994  		return nil, fmt.Errorf("scanning exit timestamp of container %s: %w", id, err)
   995  	}
   996  
   997  	result := time.Unix(timestamp, 0)
   998  
   999  	return &result, nil
  1000  }
  1001  
  1002  // PruneExitCodes removes exit codes older than 5 minutes unless the associated
  1003  // container still exists.
  1004  func (s *SQLiteState) PruneContainerExitCodes() (defErr error) {
  1005  	if !s.valid {
  1006  		return define.ErrDBClosed
  1007  	}
  1008  
  1009  	fiveMinsAgo := time.Now().Add(-5 * time.Minute).Unix()
  1010  
  1011  	tx, err := s.conn.Begin()
  1012  	if err != nil {
  1013  		return fmt.Errorf("beginning transaction to remove old timestamps: %w", err)
  1014  	}
  1015  	defer func() {
  1016  		if defErr != nil {
  1017  			if err := tx.Rollback(); err != nil {
  1018  				logrus.Errorf("Rolling back transaction to remove old timestamps: %v", err)
  1019  			}
  1020  		}
  1021  	}()
  1022  
  1023  	if _, err := tx.Exec("DELETE FROM ContainerExitCode WHERE (Timestamp <= ?) AND (ID NOT IN (SELECT ID FROM ContainerConfig))", fiveMinsAgo); err != nil {
  1024  		return fmt.Errorf("removing exit codes with timestamps older than 5 minutes: %w", err)
  1025  	}
  1026  
  1027  	if err := tx.Commit(); err != nil {
  1028  		return fmt.Errorf("committing transaction to remove old timestamps: %w", err)
  1029  	}
  1030  
  1031  	return nil
  1032  }
  1033  
  1034  // AddExecSession adds an exec session to the state.
  1035  func (s *SQLiteState) AddExecSession(ctr *Container, session *ExecSession) (defErr error) {
  1036  	if !s.valid {
  1037  		return define.ErrDBClosed
  1038  	}
  1039  
  1040  	if !ctr.valid {
  1041  		return define.ErrCtrRemoved
  1042  	}
  1043  
  1044  	tx, err := s.conn.Begin()
  1045  	if err != nil {
  1046  		return fmt.Errorf("beginning container %s exec session %s add transaction: %w", ctr.ID(), session.Id, err)
  1047  	}
  1048  	defer func() {
  1049  		if defErr != nil {
  1050  			if err := tx.Rollback(); err != nil {
  1051  				logrus.Errorf("Rolling back transaction to add container %s exec session %s: %v", ctr.ID(), session.Id, err)
  1052  			}
  1053  		}
  1054  	}()
  1055  
  1056  	if _, err := tx.Exec("INSERT INTO ContainerExecSession VALUES (?, ?);", session.Id, ctr.ID()); err != nil {
  1057  		return fmt.Errorf("adding container %s exec session %s to database: %w", ctr.ID(), session.Id, err)
  1058  	}
  1059  
  1060  	if err := tx.Commit(); err != nil {
  1061  		return fmt.Errorf("committing container %s exec session %s addition: %w", ctr.ID(), session.Id, err)
  1062  	}
  1063  
  1064  	return nil
  1065  }
  1066  
  1067  // GetExecSession returns the ID of the container an exec session is associated
  1068  // with.
  1069  func (s *SQLiteState) GetExecSession(id string) (string, error) {
  1070  	if !s.valid {
  1071  		return "", define.ErrDBClosed
  1072  	}
  1073  
  1074  	if id == "" {
  1075  		return "", define.ErrEmptyID
  1076  	}
  1077  
  1078  	row := s.conn.QueryRow("SELECT ContainerID FROM ContainerExecSession WHERE ID=?;", id)
  1079  
  1080  	var ctrID string
  1081  	if err := row.Scan(&ctrID); err != nil {
  1082  		if errors.Is(err, sql.ErrNoRows) {
  1083  			return "", fmt.Errorf("no exec session with ID %s found: %w", id, define.ErrNoSuchExecSession)
  1084  		}
  1085  		return "", fmt.Errorf("retrieving exec session %s from database: %w", id, err)
  1086  	}
  1087  
  1088  	return ctrID, nil
  1089  }
  1090  
  1091  // RemoveExecSession removes references to the given exec session in the
  1092  // database.
  1093  func (s *SQLiteState) RemoveExecSession(session *ExecSession) (defErr error) {
  1094  	if !s.valid {
  1095  		return define.ErrDBClosed
  1096  	}
  1097  
  1098  	tx, err := s.conn.Begin()
  1099  	if err != nil {
  1100  		return fmt.Errorf("beginning container %s exec session %s remove transaction: %w", session.ContainerId, session.Id, err)
  1101  	}
  1102  	defer func() {
  1103  		if defErr != nil {
  1104  			if err := tx.Rollback(); err != nil {
  1105  				logrus.Errorf("Rolling back transaction to remove container %s exec session %s: %v", session.ContainerId, session.Id, err)
  1106  			}
  1107  		}
  1108  	}()
  1109  
  1110  	result, err := tx.Exec("DELETE FROM ContainerExecSession WHERE ID=?;", session.Id)
  1111  	if err != nil {
  1112  		return fmt.Errorf("removing container %s exec session %s from database: %w", session.ContainerId, session.Id, err)
  1113  	}
  1114  	rows, err := result.RowsAffected()
  1115  	if err != nil {
  1116  		return fmt.Errorf("retrieving container %s exec session %s removal rows modified: %w", session.ContainerId, session.Id, err)
  1117  	}
  1118  	if rows == 0 {
  1119  		return define.ErrNoSuchExecSession
  1120  	}
  1121  
  1122  	if err := tx.Commit(); err != nil {
  1123  		return fmt.Errorf("committing container %s exec session %s removal: %w", session.ContainerId, session.Id, err)
  1124  	}
  1125  
  1126  	return nil
  1127  }
  1128  
  1129  // GetContainerExecSessions retrieves the IDs of all exec sessions running in a
  1130  // container that the database is aware of (IE, were added via AddExecSession).
  1131  func (s *SQLiteState) GetContainerExecSessions(ctr *Container) ([]string, error) {
  1132  	if !s.valid {
  1133  		return nil, define.ErrDBClosed
  1134  	}
  1135  
  1136  	if !ctr.valid {
  1137  		return nil, define.ErrCtrRemoved
  1138  	}
  1139  
  1140  	rows, err := s.conn.Query("SELECT ID FROM ContainerExecSession WHERE ContainerID=?;", ctr.ID())
  1141  	if err != nil {
  1142  		return nil, fmt.Errorf("querying container %s exec sessions: %w", ctr.ID(), err)
  1143  	}
  1144  	defer rows.Close()
  1145  
  1146  	var sessions []string
  1147  	for rows.Next() {
  1148  		var session string
  1149  		if err := rows.Scan(&session); err != nil {
  1150  			return nil, fmt.Errorf("scanning container %s exec sessions row: %w", ctr.ID(), err)
  1151  		}
  1152  		sessions = append(sessions, session)
  1153  	}
  1154  	if err := rows.Err(); err != nil {
  1155  		return nil, err
  1156  	}
  1157  
  1158  	return sessions, nil
  1159  }
  1160  
  1161  // RemoveContainerExecSessions removes all exec sessions attached to a given
  1162  // container.
  1163  func (s *SQLiteState) RemoveContainerExecSessions(ctr *Container) (defErr error) {
  1164  	if !s.valid {
  1165  		return define.ErrDBClosed
  1166  	}
  1167  
  1168  	if !ctr.valid {
  1169  		return define.ErrCtrRemoved
  1170  	}
  1171  
  1172  	tx, err := s.conn.Begin()
  1173  	if err != nil {
  1174  		return fmt.Errorf("beginning container %s exec session removal transaction: %w", ctr.ID(), err)
  1175  	}
  1176  	defer func() {
  1177  		if defErr != nil {
  1178  			if err := tx.Rollback(); err != nil {
  1179  				logrus.Errorf("Rolling back transaction to remove container %s exec sessions: %v", ctr.ID(), err)
  1180  			}
  1181  		}
  1182  	}()
  1183  
  1184  	if _, err := tx.Exec("DELETE FROM ContainerExecSession WHERE ContainerID=?;", ctr.ID()); err != nil {
  1185  		return fmt.Errorf("removing container %s exec sessions from database: %w", ctr.ID(), err)
  1186  	}
  1187  
  1188  	if err := tx.Commit(); err != nil {
  1189  		return fmt.Errorf("committing container %s exec session removal: %w", ctr.ID(), err)
  1190  	}
  1191  
  1192  	return nil
  1193  }
  1194  
  1195  // RewriteContainerConfig rewrites a container's configuration.
  1196  // DO NOT USE TO: Change container dependencies, change pod membership, change
  1197  // container ID.
  1198  // WARNING: This function is DANGEROUS. Do not use without reading the full
  1199  // comment on this function in state.go.
  1200  // TODO: Once BoltDB is removed, this can be combined with SafeRewriteContainerConfig.
  1201  func (s *SQLiteState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error {
  1202  	if !s.valid {
  1203  		return define.ErrDBClosed
  1204  	}
  1205  
  1206  	if !ctr.valid {
  1207  		return define.ErrCtrRemoved
  1208  	}
  1209  
  1210  	return s.rewriteContainerConfig(ctr, newCfg)
  1211  }
  1212  
  1213  // SafeRewriteContainerConfig rewrites a container's configuration in a more
  1214  // limited fashion than RewriteContainerConfig. It is marked as safe to use
  1215  // under most circumstances, unlike RewriteContainerConfig.
  1216  // DO NOT USE TO: Change container dependencies, change pod membership, change
  1217  // locks, change container ID.
  1218  // TODO: Once BoltDB is removed, this can be combined with RewriteContainerConfig.
  1219  func (s *SQLiteState) SafeRewriteContainerConfig(ctr *Container, oldName, newName string, newCfg *ContainerConfig) error {
  1220  	if !s.valid {
  1221  		return define.ErrDBClosed
  1222  	}
  1223  
  1224  	if !ctr.valid {
  1225  		return define.ErrCtrRemoved
  1226  	}
  1227  
  1228  	if newName != "" && newCfg.Name != newName {
  1229  		return fmt.Errorf("new name %s for container %s must match name in given container config: %w", newName, ctr.ID(), define.ErrInvalidArg)
  1230  	}
  1231  	if newName != "" && oldName == "" {
  1232  		return fmt.Errorf("must provide old name for container if a new name is given: %w", define.ErrInvalidArg)
  1233  	}
  1234  
  1235  	return s.rewriteContainerConfig(ctr, newCfg)
  1236  }
  1237  
  1238  // RewritePodConfig rewrites a pod's configuration.
  1239  // WARNING: This function is DANGEROUS. Do not use without reading the full
  1240  // comment on this function in state.go.
  1241  func (s *SQLiteState) RewritePodConfig(pod *Pod, newCfg *PodConfig) (defErr error) {
  1242  	if !s.valid {
  1243  		return define.ErrDBClosed
  1244  	}
  1245  
  1246  	if !pod.valid {
  1247  		return define.ErrPodRemoved
  1248  	}
  1249  
  1250  	json, err := json.Marshal(newCfg)
  1251  	if err != nil {
  1252  		return fmt.Errorf("error marshalling pod %s config JSON: %w", pod.ID(), err)
  1253  	}
  1254  
  1255  	tx, err := s.conn.Begin()
  1256  	if err != nil {
  1257  		return fmt.Errorf("beginning transaction to rewrite pod %s config: %w", pod.ID(), err)
  1258  	}
  1259  	defer func() {
  1260  		if defErr != nil {
  1261  			if err := tx.Rollback(); err != nil {
  1262  				logrus.Errorf("Rolling back transaction to rewrite pod %s config: %v", pod.ID(), err)
  1263  			}
  1264  		}
  1265  	}()
  1266  
  1267  	results, err := tx.Exec("UPDATE PodConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, pod.ID())
  1268  	if err != nil {
  1269  		return fmt.Errorf("updating pod config table with new configuration for pod %s: %w", pod.ID(), err)
  1270  	}
  1271  	rows, err := results.RowsAffected()
  1272  	if err != nil {
  1273  		return fmt.Errorf("retrieving pod %s config rewrite rows affected: %w", pod.ID(), err)
  1274  	}
  1275  	if rows == 0 {
  1276  		pod.valid = false
  1277  		return fmt.Errorf("no pod with ID %s found in DB: %w", pod.ID(), define.ErrNoSuchPod)
  1278  	}
  1279  
  1280  	if err := tx.Commit(); err != nil {
  1281  		return fmt.Errorf("committing transaction to rewrite pod %s config: %w", pod.ID(), err)
  1282  	}
  1283  
  1284  	return nil
  1285  }
  1286  
  1287  // RewriteVolumeConfig rewrites a volume's configuration.
  1288  // WARNING: This function is DANGEROUS. Do not use without reading the full
  1289  // comment on this function in state.go.
  1290  func (s *SQLiteState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig) (defErr error) {
  1291  	if !s.valid {
  1292  		return define.ErrDBClosed
  1293  	}
  1294  
  1295  	if !volume.valid {
  1296  		return define.ErrVolumeRemoved
  1297  	}
  1298  
  1299  	json, err := json.Marshal(newCfg)
  1300  	if err != nil {
  1301  		return fmt.Errorf("error marshalling volume %s new config JSON: %w", volume.Name(), err)
  1302  	}
  1303  
  1304  	tx, err := s.conn.Begin()
  1305  	if err != nil {
  1306  		return fmt.Errorf("beginning transaction to rewrite volume %s config: %w", volume.Name(), err)
  1307  	}
  1308  	defer func() {
  1309  		if defErr != nil {
  1310  			if err := tx.Rollback(); err != nil {
  1311  				logrus.Errorf("Rolling back transaction to rewrite volume %s config: %v", volume.Name(), err)
  1312  			}
  1313  		}
  1314  	}()
  1315  
  1316  	results, err := tx.Exec("UPDATE VolumeConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, volume.Name())
  1317  	if err != nil {
  1318  		return fmt.Errorf("updating volume config table with new configuration for volume %s: %w", volume.Name(), err)
  1319  	}
  1320  	rows, err := results.RowsAffected()
  1321  	if err != nil {
  1322  		return fmt.Errorf("retrieving volume %s config rewrite rows affected: %w", volume.Name(), err)
  1323  	}
  1324  	if rows == 0 {
  1325  		volume.valid = false
  1326  		return fmt.Errorf("no volume with name %q found in DB: %w", volume.Name(), define.ErrNoSuchVolume)
  1327  	}
  1328  
  1329  	if err := tx.Commit(); err != nil {
  1330  		return fmt.Errorf("committing transaction to rewrite volume %s config: %w", volume.Name(), err)
  1331  	}
  1332  
  1333  	return nil
  1334  }
  1335  
  1336  // Pod retrieves a pod given its full ID
  1337  func (s *SQLiteState) Pod(id string) (*Pod, error) {
  1338  	if id == "" {
  1339  		return nil, define.ErrEmptyID
  1340  	}
  1341  
  1342  	if !s.valid {
  1343  		return nil, define.ErrDBClosed
  1344  	}
  1345  
  1346  	row := s.conn.QueryRow("SELECT JSON FROM PodConfig WHERE ID=?;", id)
  1347  	var rawJSON string
  1348  	if err := row.Scan(&rawJSON); err != nil {
  1349  		if errors.Is(err, sql.ErrNoRows) {
  1350  			return nil, define.ErrNoSuchPod
  1351  		}
  1352  		return nil, fmt.Errorf("retrieving pod %s config from DB: %w", id, err)
  1353  	}
  1354  
  1355  	ctrCfg := new(ContainerConfig)
  1356  	if err := json.Unmarshal([]byte(rawJSON), ctrCfg); err != nil {
  1357  		return nil, fmt.Errorf("unmarshalling container %s config: %w", id, err)
  1358  	}
  1359  
  1360  	return s.createPod(rawJSON)
  1361  }
  1362  
  1363  // LookupPod retrieves a pod from a full or unique partial ID, or a name.
  1364  func (s *SQLiteState) LookupPod(idOrName string) (*Pod, error) {
  1365  	if idOrName == "" {
  1366  		return nil, define.ErrEmptyID
  1367  	}
  1368  
  1369  	if !s.valid {
  1370  		return nil, define.ErrDBClosed
  1371  	}
  1372  
  1373  	rows, err := s.conn.Query("SELECT JSON, Name FROM PodConfig WHERE PodConfig.Name=? OR (PodConfig.ID LIKE ?);", idOrName, idOrName+"%")
  1374  	if err != nil {
  1375  		return nil, fmt.Errorf("looking up pod %q in database: %w", idOrName, err)
  1376  	}
  1377  	defer rows.Close()
  1378  
  1379  	var (
  1380  		rawJSON, name string
  1381  		exactName     bool
  1382  		resCount      uint
  1383  	)
  1384  	for rows.Next() {
  1385  		if err := rows.Scan(&rawJSON, &name); err != nil {
  1386  			return nil, fmt.Errorf("error retrieving pod %q ID from database: %w", idOrName, err)
  1387  		}
  1388  		if name == idOrName {
  1389  			exactName = true
  1390  			break
  1391  		}
  1392  		resCount++
  1393  	}
  1394  	if err := rows.Err(); err != nil {
  1395  		return nil, err
  1396  	}
  1397  	if !exactName {
  1398  		if resCount == 0 {
  1399  			return nil, fmt.Errorf("no pod with name or ID %s found: %w", idOrName, define.ErrNoSuchPod)
  1400  		} else if resCount > 1 {
  1401  			return nil, fmt.Errorf("more than one result for pod %q: %w", idOrName, define.ErrCtrExists)
  1402  		}
  1403  	}
  1404  
  1405  	return s.createPod(rawJSON)
  1406  }
  1407  
  1408  // HasPod checks if a pod with the given ID exists in the state
  1409  func (s *SQLiteState) HasPod(id string) (bool, error) {
  1410  	if id == "" {
  1411  		return false, define.ErrEmptyID
  1412  	}
  1413  
  1414  	if !s.valid {
  1415  		return false, define.ErrDBClosed
  1416  	}
  1417  
  1418  	row := s.conn.QueryRow("SELECT 1 FROM PodConfig WHERE ID=?;", id)
  1419  
  1420  	var check int
  1421  	if err := row.Scan(&check); err != nil {
  1422  		if errors.Is(err, sql.ErrNoRows) {
  1423  			return false, nil
  1424  		}
  1425  		return false, fmt.Errorf("looking up pod %s in database: %w", id, err)
  1426  	} else if check != 1 {
  1427  		return false, fmt.Errorf("check digit for pod %s lookup incorrect: %w", id, define.ErrInternal)
  1428  	}
  1429  
  1430  	return true, nil
  1431  }
  1432  
  1433  // PodHasContainer checks if the given pod has a container with the given ID
  1434  func (s *SQLiteState) PodHasContainer(pod *Pod, id string) (bool, error) {
  1435  	if id == "" {
  1436  		return false, define.ErrEmptyID
  1437  	}
  1438  
  1439  	if !s.valid {
  1440  		return false, define.ErrDBClosed
  1441  	}
  1442  
  1443  	if !pod.valid {
  1444  		return false, define.ErrPodRemoved
  1445  	}
  1446  
  1447  	var check int
  1448  	row := s.conn.QueryRow("SELECT 1 FROM ContainerConfig WHERE ID=? AND PodID=?;", id, pod.ID())
  1449  	if err := row.Scan(&check); err != nil {
  1450  		if errors.Is(err, sql.ErrNoRows) {
  1451  			return false, nil
  1452  		}
  1453  		return false, fmt.Errorf("checking if pod %s has container %s in database: %w", pod.ID(), id, err)
  1454  	} else if check != 1 {
  1455  		return false, fmt.Errorf("check digit for pod %s lookup incorrect: %w", id, define.ErrInternal)
  1456  	}
  1457  
  1458  	return true, nil
  1459  }
  1460  
  1461  // PodContainersByID returns the IDs of all containers present in the given pod
  1462  func (s *SQLiteState) PodContainersByID(pod *Pod) ([]string, error) {
  1463  	if !s.valid {
  1464  		return nil, define.ErrDBClosed
  1465  	}
  1466  
  1467  	if !pod.valid {
  1468  		return nil, define.ErrPodRemoved
  1469  	}
  1470  
  1471  	rows, err := s.conn.Query("SELECT ID FROM ContainerConfig WHERE PodID=?;", pod.ID())
  1472  	if err != nil {
  1473  		return nil, fmt.Errorf("retrieving container IDs of pod %s from database: %w", pod.ID(), err)
  1474  	}
  1475  	defer rows.Close()
  1476  
  1477  	var ids []string
  1478  	for rows.Next() {
  1479  		var id string
  1480  		if err := rows.Scan(&id); err != nil {
  1481  			return nil, fmt.Errorf("scanning container from database: %w", err)
  1482  		}
  1483  
  1484  		ids = append(ids, id)
  1485  	}
  1486  	if err := rows.Err(); err != nil {
  1487  		return nil, err
  1488  	}
  1489  
  1490  	return ids, nil
  1491  }
  1492  
  1493  // PodContainers returns all the containers present in the given pod
  1494  func (s *SQLiteState) PodContainers(pod *Pod) ([]*Container, error) {
  1495  	if !s.valid {
  1496  		return nil, define.ErrDBClosed
  1497  	}
  1498  
  1499  	if !pod.valid {
  1500  		return nil, define.ErrPodRemoved
  1501  	}
  1502  
  1503  	rows, err := s.conn.Query("SELECT JSON FROM ContainerConfig WHERE PodID=?;", pod.ID())
  1504  	if err != nil {
  1505  		return nil, fmt.Errorf("retrieving containers of pod %s from database: %w", pod.ID(), err)
  1506  	}
  1507  	defer rows.Close()
  1508  
  1509  	var ctrs []*Container
  1510  	for rows.Next() {
  1511  		var rawJSON string
  1512  		if err := rows.Scan(&rawJSON); err != nil {
  1513  			return nil, fmt.Errorf("scanning container from database: %w", err)
  1514  		}
  1515  
  1516  		ctr := new(Container)
  1517  		ctr.config = new(ContainerConfig)
  1518  		ctr.state = new(ContainerState)
  1519  		ctr.runtime = s.runtime
  1520  
  1521  		if err := json.Unmarshal([]byte(rawJSON), ctr.config); err != nil {
  1522  			return nil, fmt.Errorf("unmarshalling container config: %w", err)
  1523  		}
  1524  
  1525  		ctrs = append(ctrs, ctr)
  1526  	}
  1527  	if err := rows.Err(); err != nil {
  1528  		return nil, err
  1529  	}
  1530  
  1531  	for _, ctr := range ctrs {
  1532  		if err := finalizeCtrSqlite(ctr); err != nil {
  1533  			return nil, err
  1534  		}
  1535  	}
  1536  
  1537  	return ctrs, nil
  1538  }
  1539  
  1540  // AddPod adds the given pod to the state.
  1541  func (s *SQLiteState) AddPod(pod *Pod) (defErr error) {
  1542  	if !s.valid {
  1543  		return define.ErrDBClosed
  1544  	}
  1545  
  1546  	if !pod.valid {
  1547  		return define.ErrPodRemoved
  1548  	}
  1549  
  1550  	infraID := sql.NullString{}
  1551  	if pod.state.InfraContainerID != "" {
  1552  		if err := infraID.Scan(pod.state.InfraContainerID); err != nil {
  1553  			return fmt.Errorf("scanning infra container ID %q: %w", pod.state.InfraContainerID, err)
  1554  		}
  1555  	}
  1556  
  1557  	configJSON, err := json.Marshal(pod.config)
  1558  	if err != nil {
  1559  		return fmt.Errorf("marshalling pod config json: %w", err)
  1560  	}
  1561  
  1562  	stateJSON, err := json.Marshal(pod.state)
  1563  	if err != nil {
  1564  		return fmt.Errorf("marshalling pod state json: %w", err)
  1565  	}
  1566  
  1567  	tx, err := s.conn.Begin()
  1568  	if err != nil {
  1569  		return fmt.Errorf("beginning pod create transaction: %w", err)
  1570  	}
  1571  	defer func() {
  1572  		if defErr != nil {
  1573  			if err := tx.Rollback(); err != nil {
  1574  				logrus.Errorf("Rolling back transaction to create pod: %v", err)
  1575  			}
  1576  		}
  1577  	}()
  1578  
  1579  	// TODO: explore whether there's a more idiomatic way to do error checks for the name.
  1580  	// There is a sqlite3.ErrConstraintUnique error but I (vrothberg) couldn't find a way
  1581  	// to work with the returned errors yet.
  1582  	var check int
  1583  	row := tx.QueryRow("SELECT 1 FROM PodConfig WHERE Name=?;", pod.Name())
  1584  	if err := row.Scan(&check); err != nil {
  1585  		if !errors.Is(err, sql.ErrNoRows) {
  1586  			return fmt.Errorf("checking if pod name %s exists in database: %w", pod.Name(), err)
  1587  		}
  1588  	} else if check != 0 {
  1589  		return fmt.Errorf("name %q is in use: %w", pod.Name(), define.ErrPodExists)
  1590  	}
  1591  
  1592  	if _, err := tx.Exec("INSERT INTO IDNamespace VALUES (?);", pod.ID()); err != nil {
  1593  		return fmt.Errorf("adding pod id to database: %w", err)
  1594  	}
  1595  	if _, err := tx.Exec("INSERT INTO PodConfig VALUES (?, ?, ?);", pod.ID(), pod.Name(), configJSON); err != nil {
  1596  		return fmt.Errorf("adding pod config to database: %w", err)
  1597  	}
  1598  	if _, err := tx.Exec("INSERT INTO PodState VALUES (?, ?, ?);", pod.ID(), infraID, stateJSON); err != nil {
  1599  		return fmt.Errorf("adding pod state to database: %w", err)
  1600  	}
  1601  
  1602  	if err := tx.Commit(); err != nil {
  1603  		return fmt.Errorf("committing transaction: %w", err)
  1604  	}
  1605  
  1606  	return nil
  1607  }
  1608  
  1609  // RemovePod removes the given pod from the state.
  1610  // Only empty pods can be removed.
  1611  func (s *SQLiteState) RemovePod(pod *Pod) (defErr error) {
  1612  	if !s.valid {
  1613  		return define.ErrDBClosed
  1614  	}
  1615  
  1616  	if !pod.valid {
  1617  		return define.ErrPodRemoved
  1618  	}
  1619  
  1620  	tx, err := s.conn.Begin()
  1621  	if err != nil {
  1622  		return fmt.Errorf("beginning pod %s removal transaction: %w", pod.ID(), err)
  1623  	}
  1624  	defer func() {
  1625  		if defErr != nil {
  1626  			if err := tx.Rollback(); err != nil {
  1627  				logrus.Errorf("Rolling back transaction to remove pod %s: %v", pod.ID(), err)
  1628  			}
  1629  		}
  1630  	}()
  1631  
  1632  	var check int
  1633  	row := tx.QueryRow("SELECT 1 FROM ContainerConfig WHERE PodID=? AND ID!=?;", pod.ID(), pod.state.InfraContainerID)
  1634  	if err := row.Scan(&check); err != nil {
  1635  		if !errors.Is(err, sql.ErrNoRows) {
  1636  			return fmt.Errorf("checking if pod %s has containers in database: %w", pod.ID(), err)
  1637  		}
  1638  	} else if check != 0 {
  1639  		return fmt.Errorf("pod %s is not empty: %w", pod.ID(), define.ErrCtrExists)
  1640  	}
  1641  
  1642  	checkResult := func(result sql.Result) error {
  1643  		rows, err := result.RowsAffected()
  1644  		if err != nil {
  1645  			return fmt.Errorf("retrieving pod %s delete rows affected: %w", pod.ID(), err)
  1646  		}
  1647  		if rows == 0 {
  1648  			pod.valid = false
  1649  			return define.ErrNoSuchPod
  1650  		}
  1651  		return nil
  1652  	}
  1653  
  1654  	result, err := tx.Exec("DELETE FROM IDNamespace WHERE ID=?;", pod.ID())
  1655  	if err != nil {
  1656  		return fmt.Errorf("removing pod %s id from database: %w", pod.ID(), err)
  1657  	}
  1658  	if err := checkResult(result); err != nil {
  1659  		return err
  1660  	}
  1661  
  1662  	result, err = tx.Exec("DELETE FROM PodConfig WHERE ID=?;", pod.ID())
  1663  	if err != nil {
  1664  		return fmt.Errorf("removing pod %s config from database: %w", pod.ID(), err)
  1665  	}
  1666  	if err := checkResult(result); err != nil {
  1667  		return err
  1668  	}
  1669  
  1670  	result, err = tx.Exec("DELETE FROM PodState WHERE ID=?;", pod.ID())
  1671  	if err != nil {
  1672  		return fmt.Errorf("removing pod %s state from database: %w", pod.ID(), err)
  1673  	}
  1674  	if err := checkResult(result); err != nil {
  1675  		return err
  1676  	}
  1677  
  1678  	if err := tx.Commit(); err != nil {
  1679  		return fmt.Errorf("committing pod %s removal transaction: %w", pod.ID(), err)
  1680  	}
  1681  
  1682  	return nil
  1683  }
  1684  
  1685  // RemovePodContainers removes all containers in a pod.
  1686  func (s *SQLiteState) RemovePodContainers(pod *Pod) (defErr error) {
  1687  	if !s.valid {
  1688  		return define.ErrDBClosed
  1689  	}
  1690  
  1691  	if !pod.valid {
  1692  		return define.ErrPodRemoved
  1693  	}
  1694  
  1695  	tx, err := s.conn.Begin()
  1696  	if err != nil {
  1697  		return fmt.Errorf("beginning removal transaction for containers of pod %s: %w", pod.ID(), err)
  1698  	}
  1699  	defer func() {
  1700  		if defErr != nil {
  1701  			if err := tx.Rollback(); err != nil {
  1702  				logrus.Errorf("Rolling back transaction to remove containers of pod %s: %v", pod.ID(), err)
  1703  			}
  1704  		}
  1705  	}()
  1706  
  1707  	rows, err := tx.Query("SELECT ID FROM ContainerConfig WHERE PodID=?;", pod.ID())
  1708  	if err != nil {
  1709  		return fmt.Errorf("retrieving container IDs of pod %s from database: %w", pod.ID(), err)
  1710  	}
  1711  	defer rows.Close()
  1712  
  1713  	for rows.Next() {
  1714  		var id string
  1715  		if err := rows.Scan(&id); err != nil {
  1716  			return fmt.Errorf("scanning container from database: %w", err)
  1717  		}
  1718  
  1719  		if err := s.removeContainerWithTx(id, tx); err != nil {
  1720  			return err
  1721  		}
  1722  	}
  1723  	if err := rows.Err(); err != nil {
  1724  		return err
  1725  	}
  1726  
  1727  	if err := tx.Commit(); err != nil {
  1728  		return fmt.Errorf("committing pod containers %s removal transaction: %w", pod.ID(), err)
  1729  	}
  1730  
  1731  	return nil
  1732  }
  1733  
  1734  // AddContainerToPod adds the given container to an existing pod
  1735  // The container will be added to the state and the pod
  1736  func (s *SQLiteState) AddContainerToPod(pod *Pod, ctr *Container) error {
  1737  	if !s.valid {
  1738  		return define.ErrDBClosed
  1739  	}
  1740  
  1741  	if !pod.valid {
  1742  		return define.ErrPodRemoved
  1743  	}
  1744  
  1745  	if !ctr.valid {
  1746  		return define.ErrCtrRemoved
  1747  	}
  1748  
  1749  	if ctr.config.Pod != pod.ID() {
  1750  		return fmt.Errorf("container %s is not part of pod %s: %w", ctr.ID(), pod.ID(), define.ErrNoSuchCtr)
  1751  	}
  1752  
  1753  	return s.addContainer(ctr)
  1754  }
  1755  
  1756  // RemoveContainerFromPod removes a container from an existing pod
  1757  // The container will also be removed from the state
  1758  func (s *SQLiteState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
  1759  	if !s.valid {
  1760  		return define.ErrDBClosed
  1761  	}
  1762  
  1763  	if !pod.valid {
  1764  		return define.ErrPodRemoved
  1765  	}
  1766  
  1767  	if ctr.config.Pod == "" {
  1768  		return fmt.Errorf("container %s is not part of a pod, use RemoveContainer instead: %w", ctr.ID(), define.ErrNoSuchPod)
  1769  	}
  1770  
  1771  	if ctr.config.Pod != pod.ID() {
  1772  		return fmt.Errorf("container %s is not part of pod %s: %w", ctr.ID(), pod.ID(), define.ErrInvalidArg)
  1773  	}
  1774  
  1775  	return s.removeContainer(ctr)
  1776  }
  1777  
  1778  // UpdatePod updates a pod's state from the database.
  1779  func (s *SQLiteState) UpdatePod(pod *Pod) error {
  1780  	if !s.valid {
  1781  		return define.ErrDBClosed
  1782  	}
  1783  
  1784  	if !pod.valid {
  1785  		return define.ErrPodRemoved
  1786  	}
  1787  
  1788  	row := s.conn.QueryRow("SELECT JSON FROM PodState WHERE ID=?;", pod.ID())
  1789  
  1790  	var rawJSON string
  1791  	if err := row.Scan(&rawJSON); err != nil {
  1792  		if errors.Is(err, sql.ErrNoRows) {
  1793  			// Pod was removed
  1794  			pod.valid = false
  1795  			return fmt.Errorf("no pod with ID %s found in database: %w", pod.ID(), define.ErrNoSuchPod)
  1796  		}
  1797  	}
  1798  
  1799  	newState := new(podState)
  1800  	if err := json.Unmarshal([]byte(rawJSON), newState); err != nil {
  1801  		return fmt.Errorf("unmarshalling pod %s state JSON: %w", pod.ID(), err)
  1802  	}
  1803  
  1804  	pod.state = newState
  1805  
  1806  	return nil
  1807  }
  1808  
  1809  // SavePod saves a pod's state to the database.
  1810  func (s *SQLiteState) SavePod(pod *Pod) (defErr error) {
  1811  	if !s.valid {
  1812  		return define.ErrDBClosed
  1813  	}
  1814  
  1815  	if !pod.valid {
  1816  		return define.ErrPodRemoved
  1817  	}
  1818  
  1819  	stateJSON, err := json.Marshal(pod.state)
  1820  	if err != nil {
  1821  		return fmt.Errorf("marshalling pod %s state JSON: %w", pod.ID(), err)
  1822  	}
  1823  
  1824  	tx, err := s.conn.Begin()
  1825  	if err != nil {
  1826  		return fmt.Errorf("beginning pod %s save transaction: %w", pod.ID(), err)
  1827  	}
  1828  	defer func() {
  1829  		if defErr != nil {
  1830  			if err := tx.Rollback(); err != nil {
  1831  				logrus.Errorf("Rolling back transaction to save pod %s state: %v", pod.ID(), err)
  1832  			}
  1833  		}
  1834  	}()
  1835  
  1836  	result, err := tx.Exec("UPDATE PodState SET JSON=? WHERE ID=?;", stateJSON, pod.ID())
  1837  	if err != nil {
  1838  		return fmt.Errorf("writing pod %s state: %w", pod.ID(), err)
  1839  	}
  1840  	rows, err := result.RowsAffected()
  1841  	if err != nil {
  1842  		return fmt.Errorf("retrieving pod %s save rows affected: %w", pod.ID(), err)
  1843  	}
  1844  	if rows == 0 {
  1845  		pod.valid = false
  1846  		return define.ErrNoSuchPod
  1847  	}
  1848  
  1849  	if err := tx.Commit(); err != nil {
  1850  		return fmt.Errorf("committing pod %s state: %w", pod.ID(), err)
  1851  	}
  1852  
  1853  	return nil
  1854  }
  1855  
  1856  // AllPods returns all pods present in the state.
  1857  func (s *SQLiteState) AllPods() ([]*Pod, error) {
  1858  	if !s.valid {
  1859  		return nil, define.ErrDBClosed
  1860  	}
  1861  
  1862  	pods := []*Pod{}
  1863  	rows, err := s.conn.Query("SELECT JSON FROM PodConfig;")
  1864  	if err != nil {
  1865  		return nil, fmt.Errorf("retrieving all pods from database: %w", err)
  1866  	}
  1867  	defer rows.Close()
  1868  
  1869  	for rows.Next() {
  1870  		var rawJSON string
  1871  		if err := rows.Scan(&rawJSON); err != nil {
  1872  			return nil, fmt.Errorf("scanning pod from database: %w", err)
  1873  		}
  1874  
  1875  		pod, err := s.createPod(rawJSON)
  1876  		if err != nil {
  1877  			return nil, err
  1878  		}
  1879  
  1880  		pods = append(pods, pod)
  1881  	}
  1882  	if err := rows.Err(); err != nil {
  1883  		return nil, err
  1884  	}
  1885  
  1886  	return pods, nil
  1887  }
  1888  
  1889  // AddVolume adds the given volume to the state. It also adds ctrDepID to
  1890  // the sub bucket holding the container dependencies that this volume has
  1891  func (s *SQLiteState) AddVolume(volume *Volume) (defErr error) {
  1892  	if !s.valid {
  1893  		return define.ErrDBClosed
  1894  	}
  1895  
  1896  	if !volume.valid {
  1897  		return define.ErrVolumeRemoved
  1898  	}
  1899  
  1900  	cfgJSON, err := json.Marshal(volume.config)
  1901  	if err != nil {
  1902  		return fmt.Errorf("marshalling volume %s configuration json: %w", volume.Name(), err)
  1903  	}
  1904  
  1905  	volState := volume.state
  1906  	if volState == nil {
  1907  		volState = new(VolumeState)
  1908  	}
  1909  
  1910  	stateJSON, err := json.Marshal(volState)
  1911  	if err != nil {
  1912  		return fmt.Errorf("marshalling volume %s state json: %w", volume.Name(), err)
  1913  	}
  1914  
  1915  	storageID := sql.NullString{}
  1916  	if volume.config.StorageID != "" {
  1917  		storageID.Valid = true
  1918  		storageID.String = volume.config.StorageID
  1919  	}
  1920  
  1921  	tx, err := s.conn.Begin()
  1922  	if err != nil {
  1923  		return fmt.Errorf("beginning volume create transaction: %w", err)
  1924  	}
  1925  	defer func() {
  1926  		if defErr != nil {
  1927  			if err := tx.Rollback(); err != nil {
  1928  				logrus.Errorf("Rolling back transaction to create volume: %v", err)
  1929  			}
  1930  		}
  1931  	}()
  1932  
  1933  	// TODO: There has to be a better way of doing this
  1934  	var check int
  1935  	row := tx.QueryRow("SELECT 1 FROM VolumeConfig WHERE Name=?;", volume.Name())
  1936  	if err := row.Scan(&check); err != nil {
  1937  		if !errors.Is(err, sql.ErrNoRows) {
  1938  			return fmt.Errorf("checking if volume name %s exists in database: %w", volume.Name(), err)
  1939  		}
  1940  	} else if check != 0 {
  1941  		return fmt.Errorf("name %q is in use: %w", volume.Name(), define.ErrVolumeExists)
  1942  	}
  1943  
  1944  	if _, err := tx.Exec("INSERT INTO VolumeConfig VALUES (?, ?, ?);", volume.Name(), storageID, cfgJSON); err != nil {
  1945  		return fmt.Errorf("adding volume %s config to database: %w", volume.Name(), err)
  1946  	}
  1947  
  1948  	if _, err := tx.Exec("INSERT INTO VolumeState VALUES (?, ?);", volume.Name(), stateJSON); err != nil {
  1949  		return fmt.Errorf("adding volume %s state to database: %w", volume.Name(), err)
  1950  	}
  1951  
  1952  	if err := tx.Commit(); err != nil {
  1953  		return fmt.Errorf("committing transaction: %w", err)
  1954  	}
  1955  
  1956  	return nil
  1957  }
  1958  
  1959  // RemoveVolume removes the given volume from the state
  1960  func (s *SQLiteState) RemoveVolume(volume *Volume) (defErr error) {
  1961  	if !s.valid {
  1962  		return define.ErrDBClosed
  1963  	}
  1964  
  1965  	tx, err := s.conn.Begin()
  1966  	if err != nil {
  1967  		return fmt.Errorf("beginning volume %s removal transaction: %w", volume.Name(), err)
  1968  	}
  1969  	defer func() {
  1970  		if defErr != nil {
  1971  			if err := tx.Rollback(); err != nil {
  1972  				logrus.Errorf("Rolling back transaction to remove volume %s: %v", volume.Name(), err)
  1973  			}
  1974  		}
  1975  	}()
  1976  
  1977  	rows, err := tx.Query("SELECT ContainerID FROM ContainerVolume WHERE VolumeName=?;", volume.Name())
  1978  	if err != nil {
  1979  		return fmt.Errorf("querying for containers using volume %s: %w", volume.Name(), err)
  1980  	}
  1981  	defer rows.Close()
  1982  
  1983  	var ctrs []string
  1984  	for rows.Next() {
  1985  		var ctr string
  1986  		if err := rows.Scan(&ctr); err != nil {
  1987  			return fmt.Errorf("error scanning row for containers using volume %s: %w", volume.Name(), err)
  1988  		}
  1989  		ctrs = append(ctrs, ctr)
  1990  	}
  1991  	if err := rows.Err(); err != nil {
  1992  		return err
  1993  	}
  1994  	if len(ctrs) > 0 {
  1995  		return fmt.Errorf("volume %s is in use by containers %s: %w", volume.Name(), strings.Join(ctrs, ","), define.ErrVolumeBeingUsed)
  1996  	}
  1997  
  1998  	// TODO TODO TODO:
  1999  	// Need to verify that at least 1 row was deleted from VolumeConfig.
  2000  	// Otherwise return ErrNoSuchVolume
  2001  
  2002  	if _, err := tx.Exec("DELETE FROM VolumeConfig WHERE Name=?;", volume.Name()); err != nil {
  2003  		return fmt.Errorf("removing volume %s config from DB: %w", volume.Name(), err)
  2004  	}
  2005  
  2006  	if _, err := tx.Exec("DELETE FROM VolumeState WHERE Name=?;", volume.Name()); err != nil {
  2007  		return fmt.Errorf("removing volume %s state from DB: %w", volume.Name(), err)
  2008  	}
  2009  
  2010  	if err := tx.Commit(); err != nil {
  2011  		return fmt.Errorf("committing transaction to remove volume %s: %w", volume.Name(), err)
  2012  	}
  2013  
  2014  	return nil
  2015  }
  2016  
  2017  // UpdateVolume updates the volume's state from the database.
  2018  func (s *SQLiteState) UpdateVolume(volume *Volume) error {
  2019  	if !s.valid {
  2020  		return define.ErrDBClosed
  2021  	}
  2022  
  2023  	if !volume.valid {
  2024  		return define.ErrVolumeRemoved
  2025  	}
  2026  
  2027  	row := s.conn.QueryRow("SELECT JSON FROM VolumeState WHERE Name=?;", volume.Name())
  2028  
  2029  	var stateJSON string
  2030  	if err := row.Scan(&stateJSON); err != nil {
  2031  		if errors.Is(err, sql.ErrNoRows) {
  2032  			volume.valid = false
  2033  			return define.ErrNoSuchVolume
  2034  		}
  2035  		return fmt.Errorf("scanning volume %s state JSON: %w", volume.Name(), err)
  2036  	}
  2037  
  2038  	newState := new(VolumeState)
  2039  	if err := json.Unmarshal([]byte(stateJSON), newState); err != nil {
  2040  		return fmt.Errorf("unmarshalling volume %s state: %w", volume.Name(), err)
  2041  	}
  2042  
  2043  	volume.state = newState
  2044  
  2045  	return nil
  2046  }
  2047  
  2048  // SaveVolume saves the volume's state to the database.
  2049  func (s *SQLiteState) SaveVolume(volume *Volume) (defErr error) {
  2050  	if !s.valid {
  2051  		return define.ErrDBClosed
  2052  	}
  2053  
  2054  	if !volume.valid {
  2055  		return define.ErrVolumeRemoved
  2056  	}
  2057  
  2058  	stateJSON, err := json.Marshal(volume.state)
  2059  	if err != nil {
  2060  		return fmt.Errorf("marshalling volume %s state JSON: %w", volume.Name(), err)
  2061  	}
  2062  
  2063  	tx, err := s.conn.Begin()
  2064  	if err != nil {
  2065  		return fmt.Errorf("beginning transaction to rewrite volume %s state: %w", volume.Name(), err)
  2066  	}
  2067  	defer func() {
  2068  		if defErr != nil {
  2069  			if err := tx.Rollback(); err != nil {
  2070  				logrus.Errorf("Rolling back transaction to rewrite volume %s state: %v", volume.Name(), err)
  2071  			}
  2072  		}
  2073  	}()
  2074  
  2075  	results, err := tx.Exec("UPDATE VolumeState SET JSON=? WHERE Name=?;", stateJSON, volume.Name())
  2076  	if err != nil {
  2077  		return fmt.Errorf("updating volume %s state in DB: %w", volume.Name(), err)
  2078  	}
  2079  	rows, err := results.RowsAffected()
  2080  	if err != nil {
  2081  		return fmt.Errorf("retrieving volume %s state rewrite rows affected: %w", volume.Name(), err)
  2082  	}
  2083  	if rows == 0 {
  2084  		volume.valid = false
  2085  		return define.ErrNoSuchVolume
  2086  	}
  2087  
  2088  	if err := tx.Commit(); err != nil {
  2089  		return fmt.Errorf("committing transaction to rewrite volume %s state: %w", volume.Name(), err)
  2090  	}
  2091  
  2092  	return nil
  2093  }
  2094  
  2095  // AllVolumes returns all volumes present in the state.
  2096  func (s *SQLiteState) AllVolumes() ([]*Volume, error) {
  2097  	if !s.valid {
  2098  		return nil, define.ErrDBClosed
  2099  	}
  2100  
  2101  	rows, err := s.conn.Query("SELECT JSON FROM VolumeConfig;")
  2102  	if err != nil {
  2103  		return nil, fmt.Errorf("querying database for all volumes: %w", err)
  2104  	}
  2105  	defer rows.Close()
  2106  
  2107  	var volumes []*Volume
  2108  
  2109  	for rows.Next() {
  2110  		var configJSON string
  2111  		if err := rows.Scan(&configJSON); err != nil {
  2112  			return nil, fmt.Errorf("scanning volume config from database: %w", err)
  2113  		}
  2114  		vol := new(Volume)
  2115  		vol.config = new(VolumeConfig)
  2116  		vol.state = new(VolumeState)
  2117  		vol.runtime = s.runtime
  2118  
  2119  		if err := json.Unmarshal([]byte(configJSON), vol.config); err != nil {
  2120  			return nil, fmt.Errorf("unmarshalling volume config: %w", err)
  2121  		}
  2122  
  2123  		if err := finalizeVolumeSqlite(vol); err != nil {
  2124  			return nil, err
  2125  		}
  2126  
  2127  		volumes = append(volumes, vol)
  2128  	}
  2129  	if err := rows.Err(); err != nil {
  2130  		return nil, err
  2131  	}
  2132  
  2133  	return volumes, nil
  2134  }
  2135  
  2136  // Volume retrieves a volume from full name.
  2137  func (s *SQLiteState) Volume(name string) (*Volume, error) {
  2138  	if name == "" {
  2139  		return nil, define.ErrEmptyID
  2140  	}
  2141  
  2142  	if !s.valid {
  2143  		return nil, define.ErrDBClosed
  2144  	}
  2145  
  2146  	row := s.conn.QueryRow("SELECT JSON FROM VolumeConfig WHERE Name=?;", name)
  2147  
  2148  	var configJSON string
  2149  
  2150  	if err := row.Scan(&configJSON); err != nil {
  2151  		if errors.Is(err, sql.ErrNoRows) {
  2152  			return nil, define.ErrNoSuchVolume
  2153  		}
  2154  	}
  2155  
  2156  	vol := new(Volume)
  2157  	vol.config = new(VolumeConfig)
  2158  	vol.state = new(VolumeState)
  2159  	vol.runtime = s.runtime
  2160  
  2161  	if err := json.Unmarshal([]byte(configJSON), vol.config); err != nil {
  2162  		return nil, fmt.Errorf("unmarshalling volume %s config JSON: %w", name, err)
  2163  	}
  2164  
  2165  	if err := finalizeVolumeSqlite(vol); err != nil {
  2166  		return nil, err
  2167  	}
  2168  
  2169  	return vol, nil
  2170  }
  2171  
  2172  // LookupVolume locates a volume from a unique partial name.
  2173  func (s *SQLiteState) LookupVolume(name string) (*Volume, error) {
  2174  	if name == "" {
  2175  		return nil, define.ErrEmptyID
  2176  	}
  2177  
  2178  	if !s.valid {
  2179  		return nil, define.ErrDBClosed
  2180  	}
  2181  
  2182  	rows, err := s.conn.Query("SELECT Name, JSON FROM VolumeConfig WHERE Name LIKE ? ORDER BY LENGTH(Name) ASC;", name+"%")
  2183  	if err != nil {
  2184  		return nil, fmt.Errorf("querying database for volume %s: %w", name, err)
  2185  	}
  2186  	defer rows.Close()
  2187  
  2188  	var foundName, configJSON string
  2189  	for rows.Next() {
  2190  		if foundName != "" {
  2191  			return nil, fmt.Errorf("more than one result for volume name %s: %w", name, define.ErrVolumeExists)
  2192  		}
  2193  		if err := rows.Scan(&foundName, &configJSON); err != nil {
  2194  			return nil, fmt.Errorf("retrieving volume %s config from database: %w", name, err)
  2195  		}
  2196  		if foundName == name {
  2197  			break
  2198  		}
  2199  	}
  2200  	if err := rows.Err(); err != nil {
  2201  		return nil, err
  2202  	}
  2203  	if foundName == "" {
  2204  		return nil, fmt.Errorf("no volume with name %q found: %w", name, define.ErrNoSuchVolume)
  2205  	}
  2206  
  2207  	vol := new(Volume)
  2208  	vol.config = new(VolumeConfig)
  2209  	vol.state = new(VolumeState)
  2210  	vol.runtime = s.runtime
  2211  
  2212  	if err := json.Unmarshal([]byte(configJSON), vol.config); err != nil {
  2213  		return nil, fmt.Errorf("unmarshalling volume %s config JSON: %w", name, err)
  2214  	}
  2215  
  2216  	if err := finalizeVolumeSqlite(vol); err != nil {
  2217  		return nil, err
  2218  	}
  2219  
  2220  	return vol, nil
  2221  }
  2222  
  2223  // HasVolume returns true if the given volume exists in the state.
  2224  // Otherwise it returns false.
  2225  func (s *SQLiteState) HasVolume(name string) (bool, error) {
  2226  	if name == "" {
  2227  		return false, define.ErrEmptyID
  2228  	}
  2229  
  2230  	if !s.valid {
  2231  		return false, define.ErrDBClosed
  2232  	}
  2233  
  2234  	row := s.conn.QueryRow("SELECT 1 FROM VolumeConfig WHERE Name=?;", name)
  2235  
  2236  	var check int
  2237  	if err := row.Scan(&check); err != nil {
  2238  		if errors.Is(err, sql.ErrNoRows) {
  2239  			return false, nil
  2240  		}
  2241  		return false, fmt.Errorf("looking up volume %s in database: %w", name, err)
  2242  	}
  2243  	if check != 1 {
  2244  		return false, fmt.Errorf("check digit for volume %s lookup incorrect: %w", name, define.ErrInternal)
  2245  	}
  2246  
  2247  	return true, nil
  2248  }
  2249  
  2250  // VolumeInUse checks if any container is using the volume.
  2251  // It returns a slice of the IDs of the containers using the given
  2252  // volume. If the slice is empty, no containers use the given volume.
  2253  func (s *SQLiteState) VolumeInUse(volume *Volume) ([]string, error) {
  2254  	if !s.valid {
  2255  		return nil, define.ErrDBClosed
  2256  	}
  2257  
  2258  	if !volume.valid {
  2259  		return nil, define.ErrVolumeRemoved
  2260  	}
  2261  
  2262  	rows, err := s.conn.Query("SELECT ContainerID FROM ContainerVolume WHERE VolumeName=?;", volume.Name())
  2263  	if err != nil {
  2264  		return nil, fmt.Errorf("querying database for containers using volume %s: %w", volume.Name(), err)
  2265  	}
  2266  	defer rows.Close()
  2267  
  2268  	var ctrs []string
  2269  	for rows.Next() {
  2270  		var ctr string
  2271  		if err := rows.Scan(&ctr); err != nil {
  2272  			return nil, fmt.Errorf("scanning container ID for container using volume %s: %w", volume.Name(), err)
  2273  		}
  2274  		ctrs = append(ctrs, ctr)
  2275  	}
  2276  	if err := rows.Err(); err != nil {
  2277  		return nil, err
  2278  	}
  2279  
  2280  	return ctrs, nil
  2281  }
  2282  
  2283  // ContainerIDIsVolume checks if the given c/storage container ID is used as
  2284  // backing storage for a volume.
  2285  func (s *SQLiteState) ContainerIDIsVolume(id string) (bool, error) {
  2286  	if !s.valid {
  2287  		return false, define.ErrDBClosed
  2288  	}
  2289  
  2290  	row := s.conn.QueryRow("SELECT 1 FROM VolumeConfig WHERE StorageID=?;", id)
  2291  	var checkDigit int
  2292  	if err := row.Scan(&checkDigit); err != nil {
  2293  		if errors.Is(err, sql.ErrNoRows) {
  2294  			return false, nil
  2295  		}
  2296  		return false, fmt.Errorf("error retrieving volumes using storage ID %s: %w", id, err)
  2297  	}
  2298  	if checkDigit != 1 {
  2299  		return false, fmt.Errorf("check digit for volumes using storage ID %s was incorrect: %w", id, define.ErrInternal)
  2300  	}
  2301  
  2302  	return true, nil
  2303  }