16
"github.com/containers/common/libnetwork/types"
17
"github.com/containers/podman/v5/libpod/define"
18
"github.com/containers/storage"
19
"github.com/sirupsen/logrus"
21
// SQLite backend for database/sql
22
_ "github.com/mattn/go-sqlite3"
25
const schemaVersion = 1
27
// SQLiteState is a state implementation backed by a SQLite database
28
type SQLiteState struct {
35
// Deal with timezone automatically.
36
sqliteOptionLocation = "_loc=auto"
37
// Force an fsync after each transaction (https://www.sqlite.org/pragma.html#pragma_synchronous).
38
sqliteOptionSynchronous = "&_sync=FULL"
39
// Allow foreign keys (https://www.sqlite.org/pragma.html#pragma_foreign_keys).
40
sqliteOptionForeignKeys = "&_foreign_keys=1"
41
// Make sure that transactions happen exclusively.
42
sqliteOptionTXLock = "&_txlock=exclusive"
43
// Make sure busy timeout is set to high value to keep retrying when the db is locked.
44
// Timeout is in ms, so set it to 100s to have enough time to retry the operations.
45
sqliteOptionBusyTimeout = "&_busy_timeout=100000"
47
// Assembled sqlite options used when opening the database.
48
sqliteOptions = "db.sql?" +
49
sqliteOptionLocation +
50
sqliteOptionSynchronous +
51
sqliteOptionForeignKeys +
53
sqliteOptionBusyTimeout
56
// NewSqliteState creates a new SQLite-backed state database.
57
func NewSqliteState(runtime *Runtime) (_ State, defErr error) {
58
logrus.Info("Using sqlite as database backend")
59
state := new(SQLiteState)
61
basePath := runtime.storageConfig.GraphRoot
62
if runtime.storageConfig.TransientStore {
63
basePath = runtime.storageConfig.RunRoot
64
} else if !runtime.storageSet.StaticDirSet {
65
basePath = runtime.config.Engine.StaticDir
68
// c/storage is set up *after* the DB - so even though we use the c/s
69
// root (or, for transient, runroot) dir, we need to make the dir
71
if err := os.MkdirAll(basePath, 0700); err != nil {
72
return nil, fmt.Errorf("creating root directory: %w", err)
75
conn, err := sql.Open("sqlite3", filepath.Join(basePath, sqliteOptions))
77
return nil, fmt.Errorf("initializing sqlite database: %w", err)
81
if err := conn.Close(); err != nil {
82
logrus.Errorf("Error closing SQLite DB connection: %v", err)
87
if err := initSQLiteDB(conn); err != nil {
93
state.runtime = runtime
98
// Close closes the state and prevents further use
99
func (s *SQLiteState) Close() error {
100
if err := s.conn.Close(); err != nil {
108
// Refresh clears container and pod states after a reboot
109
func (s *SQLiteState) Refresh() (defErr error) {
111
return define.ErrDBClosed
114
// Retrieve all containers, pods, and volumes.
115
// Maps are indexed by ID (or volume name) so we know which goes where,
116
// and store the marshalled state JSON
117
ctrStates := make(map[string]string)
118
podStates := make(map[string]string)
119
volumeStates := make(map[string]string)
121
ctrRows, err := s.conn.Query("SELECT ID, JSON FROM ContainerState;")
123
return fmt.Errorf("querying for container states: %w", err)
125
defer ctrRows.Close()
131
if err := ctrRows.Scan(&id, &stateJSON); err != nil {
132
return fmt.Errorf("scanning container state row: %w", err)
135
ctrState := new(ContainerState)
137
if err := json.Unmarshal([]byte(stateJSON), ctrState); err != nil {
138
return fmt.Errorf("unmarshalling container state json: %w", err)
142
resetContainerState(ctrState)
144
newJSON, err := json.Marshal(ctrState)
146
return fmt.Errorf("marshalling container state json: %w", err)
149
ctrStates[id] = string(newJSON)
151
if err := ctrRows.Err(); err != nil {
155
podRows, err := s.conn.Query("SELECT ID, JSON FROM PodState;")
157
return fmt.Errorf("querying for pod states: %w", err)
159
defer podRows.Close()
165
if err := podRows.Scan(&id, &stateJSON); err != nil {
166
return fmt.Errorf("scanning pod state row: %w", err)
169
podState := new(podState)
171
if err := json.Unmarshal([]byte(stateJSON), podState); err != nil {
172
return fmt.Errorf("unmarshalling pod state json: %w", err)
176
resetPodState(podState)
178
newJSON, err := json.Marshal(podState)
180
return fmt.Errorf("marshalling pod state json: %w", err)
183
podStates[id] = string(newJSON)
185
if err := podRows.Err(); err != nil {
189
volRows, err := s.conn.Query("SELECT Name, JSON FROM VolumeState;")
191
return fmt.Errorf("querying for volume states: %w", err)
193
defer volRows.Close()
197
name, stateJSON string
200
if err := volRows.Scan(&name, &stateJSON); err != nil {
201
return fmt.Errorf("scanning volume state row: %w", err)
204
volState := new(VolumeState)
206
if err := json.Unmarshal([]byte(stateJSON), volState); err != nil {
207
return fmt.Errorf("unmarshalling volume state json: %w", err)
211
resetVolumeState(volState)
213
newJSON, err := json.Marshal(volState)
215
return fmt.Errorf("marshalling volume state json: %w", err)
218
volumeStates[name] = string(newJSON)
220
if err := volRows.Err(); err != nil {
224
// Write updated states back to DB, and perform additional maintenance
225
// (Remove exit codes and exec sessions)
227
tx, err := s.conn.Begin()
229
return fmt.Errorf("beginning refresh transaction: %w", err)
233
if err := tx.Rollback(); err != nil {
234
logrus.Errorf("Rolling back transaction to refresh database state: %v", err)
239
for id, json := range ctrStates {
240
if _, err := tx.Exec("UPDATE ContainerState SET JSON=? WHERE ID=?;", json, id); err != nil {
241
return fmt.Errorf("updating container state: %w", err)
244
for id, json := range podStates {
245
if _, err := tx.Exec("UPDATE PodState SET JSON=? WHERE ID=?;", json, id); err != nil {
246
return fmt.Errorf("updating pod state: %w", err)
249
for name, json := range volumeStates {
250
if _, err := tx.Exec("UPDATE VolumeState SET JSON=? WHERE Name=?;", json, name); err != nil {
251
return fmt.Errorf("updating volume state: %w", err)
255
if _, err := tx.Exec("DELETE FROM ContainerExitCode;"); err != nil {
256
return fmt.Errorf("removing container exit codes: %w", err)
259
if _, err := tx.Exec("DELETE FROM ContainerExecSession;"); err != nil {
260
return fmt.Errorf("removing container exec sessions: %w", err)
263
if err := tx.Commit(); err != nil {
264
return fmt.Errorf("committing transaction: %w", err)
270
// GetDBConfig retrieves runtime configuration fields that were created when
271
// the database was first initialized
272
func (s *SQLiteState) GetDBConfig() (*DBConfig, error) {
274
return nil, define.ErrDBClosed
278
var staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumeDir string
280
row := s.conn.QueryRow("SELECT StaticDir, TmpDir, GraphRoot, RunRoot, GraphDriver, VolumeDir FROM DBConfig;")
282
if err := row.Scan(&staticDir, &tmpDir, &graphRoot, &runRoot, &graphDriver, &volumeDir); err != nil {
283
if errors.Is(err, sql.ErrNoRows) {
286
return nil, fmt.Errorf("retrieving DB config: %w", err)
289
cfg.LibpodRoot = staticDir
290
cfg.LibpodTmp = tmpDir
291
cfg.StorageRoot = graphRoot
292
cfg.StorageTmp = runRoot
293
cfg.GraphDriver = graphDriver
294
cfg.VolumePath = volumeDir
299
// ValidateDBConfig validates paths in the given runtime against the database
300
func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
302
return define.ErrDBClosed
305
storeOpts, err := storage.DefaultStoreOptions()
311
INSERT INTO DBconfig VALUES (
318
dbOS, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
319
runtimeOS = goruntime.GOOS
320
runtimeStaticDir = filepath.Clean(s.runtime.config.Engine.StaticDir)
321
runtimeTmpDir = filepath.Clean(s.runtime.config.Engine.TmpDir)
322
runtimeGraphRoot = filepath.Clean(s.runtime.StorageConfig().GraphRoot)
323
runtimeRunRoot = filepath.Clean(s.runtime.StorageConfig().RunRoot)
324
runtimeGraphDriver = s.runtime.StorageConfig().GraphDriverName
325
runtimeVolumePath = filepath.Clean(s.runtime.config.Engine.VolumePath)
328
// Some fields may be empty, indicating they are set to the default.
329
// If so, grab the default from c/storage for them.
330
if runtimeGraphRoot == "" {
331
runtimeGraphRoot = storeOpts.GraphRoot
333
if runtimeRunRoot == "" {
334
runtimeRunRoot = storeOpts.RunRoot
336
if runtimeGraphDriver == "" {
337
runtimeGraphDriver = storeOpts.GraphDriverName
340
// We have to do this in a transaction to ensure mutual exclusion.
341
// Otherwise we have a race - multiple processes can be checking the
342
// row's existence simultaneously, both try to create it, second one to
343
// get the transaction lock gets an error.
344
// TODO: The transaction isn't strictly necessary, and there's a (small)
345
// chance it's a perf hit. If it is, we can move it entirely within the
346
// `errors.Is()` block below, with extra validation to ensure the row
347
// still does not exist (and, if it does, to retry this function).
348
tx, err := s.conn.Begin()
350
return fmt.Errorf("beginning database validation transaction: %w", err)
354
if err := tx.Rollback(); err != nil {
355
logrus.Errorf("Rolling back transaction to validate database: %v", err)
360
row := tx.QueryRow("SELECT Os, StaticDir, TmpDir, GraphRoot, RunRoot, GraphDriver, VolumeDir FROM DBConfig;")
362
if err := row.Scan(&dbOS, &staticDir, &tmpDir, &graphRoot, &runRoot, &graphDriver, &volumePath); err != nil {
363
if errors.Is(err, sql.ErrNoRows) {
364
if _, err := tx.Exec(createRow, 1, schemaVersion, runtimeOS,
365
runtimeStaticDir, runtimeTmpDir, runtimeGraphRoot,
366
runtimeRunRoot, runtimeGraphDriver, runtimeVolumePath); err != nil {
367
return fmt.Errorf("adding DB config row: %w", err)
370
if err := tx.Commit(); err != nil {
371
return fmt.Errorf("committing write of database validation row: %w", err)
377
return fmt.Errorf("retrieving DB config: %w", err)
380
checkField := func(fieldName, dbVal, ourVal string, isPath bool) error {
382
// Evaluate symlinks. Ignore ENOENT. No guarantee all
383
// directories exist this early in Libpod init.
385
dbValClean, err := filepath.EvalSymlinks(dbVal)
386
if err != nil && !errors.Is(err, fs.ErrNotExist) {
387
return fmt.Errorf("cannot evaluate symlinks on DB %s path %q: %w", fieldName, dbVal, err)
392
ourValClean, err := filepath.EvalSymlinks(ourVal)
393
if err != nil && !errors.Is(err, fs.ErrNotExist) {
394
return fmt.Errorf("cannot evaluate symlinks on our %s path %q: %w", fieldName, ourVal, err)
401
return fmt.Errorf("database %s %q does not match our %s %q: %w", fieldName, dbVal, fieldName, ourVal, define.ErrDBBadConfig)
407
if err := checkField("os", dbOS, runtimeOS, false); err != nil {
410
if err := checkField("static dir", staticDir, runtimeStaticDir, true); err != nil {
413
if err := checkField("tmp dir", tmpDir, runtimeTmpDir, true); err != nil {
416
if err := checkField("graph root", graphRoot, runtimeGraphRoot, true); err != nil {
419
if err := checkField("run root", runRoot, runtimeRunRoot, true); err != nil {
422
if err := checkField("graph driver", graphDriver, runtimeGraphDriver, false); err != nil {
425
if err := checkField("volume path", volumePath, runtimeVolumePath, true); err != nil {
429
if err := tx.Commit(); err != nil {
430
return fmt.Errorf("committing database validation row: %w", err)
432
// Do not return any error after the commit call because the defer will
433
// try to roll back the transaction which results in an logged error.
438
// GetContainerName returns the name of the container associated with a given
439
// ID. Returns ErrNoSuchCtr if the ID does not exist.
440
func (s *SQLiteState) GetContainerName(id string) (string, error) {
442
return "", define.ErrEmptyID
446
return "", define.ErrDBClosed
451
row := s.conn.QueryRow("SELECT Name FROM ContainerConfig WHERE ID=?;", id)
452
if err := row.Scan(&name); err != nil {
453
if errors.Is(err, sql.ErrNoRows) {
454
return "", define.ErrNoSuchCtr
457
return "", fmt.Errorf("looking up container %s name: %w", id, err)
463
// GetPodName returns the name of the pod associated with a given ID.
464
// Returns ErrNoSuchPod if the ID does not exist.
465
func (s *SQLiteState) GetPodName(id string) (string, error) {
467
return "", define.ErrEmptyID
471
return "", define.ErrDBClosed
476
row := s.conn.QueryRow("SELECT Name FROM PodConfig WHERE ID=?;", id)
477
if err := row.Scan(&name); err != nil {
478
if errors.Is(err, sql.ErrNoRows) {
479
return "", define.ErrNoSuchPod
482
return "", fmt.Errorf("looking up pod %s name: %w", id, err)
488
// Container retrieves a single container from the state by its full ID
489
func (s *SQLiteState) Container(id string) (*Container, error) {
491
return nil, define.ErrEmptyID
495
return nil, define.ErrDBClosed
498
ctrConfig, err := s.getCtrConfig(id)
503
ctr := new(Container)
504
ctr.config = ctrConfig
505
ctr.state = new(ContainerState)
506
ctr.runtime = s.runtime
508
if err := finalizeCtrSqlite(ctr); err != nil {
515
// LookupContainerID retrieves a container ID from the state by full or unique
517
func (s *SQLiteState) LookupContainerID(idOrName string) (string, error) {
519
return "", define.ErrEmptyID
523
return "", define.ErrDBClosed
526
rows, err := s.conn.Query("SELECT ID, Name FROM ContainerConfig WHERE ContainerConfig.Name=? OR (ContainerConfig.ID LIKE ?);", idOrName, idOrName+"%")
528
return "", fmt.Errorf("looking up container %q in database: %w", idOrName, err)
537
if err := rows.Scan(&id, &name); err != nil {
538
return "", fmt.Errorf("retrieving container %q ID from database: %w", idOrName, err)
540
if name == idOrName {
545
if err := rows.Err(); err != nil {
549
return "", define.ErrNoSuchCtr
550
} else if resCount > 1 {
551
return "", fmt.Errorf("more than one result for container %q: %w", idOrName, define.ErrCtrExists)
557
// LookupContainer retrieves a container from the state by full or unique
559
func (s *SQLiteState) LookupContainer(idOrName string) (*Container, error) {
561
return nil, define.ErrEmptyID
565
return nil, define.ErrDBClosed
568
rows, err := s.conn.Query("SELECT JSON, Name FROM ContainerConfig WHERE ContainerConfig.Name=? OR (ContainerConfig.ID LIKE ?);", idOrName, idOrName+"%")
570
return nil, fmt.Errorf("looking up container %q in database: %w", idOrName, err)
580
if err := rows.Scan(&rawJSON, &name); err != nil {
581
return nil, fmt.Errorf("retrieving container %q ID from database: %w", idOrName, err)
583
if name == idOrName {
589
if err := rows.Err(); err != nil {
594
return nil, fmt.Errorf("no container with name or ID %q found: %w", idOrName, define.ErrNoSuchCtr)
595
} else if resCount > 1 {
596
return nil, fmt.Errorf("more than one result for container %q: %w", idOrName, define.ErrCtrExists)
600
ctr := new(Container)
601
ctr.config = new(ContainerConfig)
602
ctr.state = new(ContainerState)
603
ctr.runtime = s.runtime
605
if err := json.Unmarshal([]byte(rawJSON), ctr.config); err != nil {
606
return nil, fmt.Errorf("unmarshalling container config JSON: %w", err)
609
if err := finalizeCtrSqlite(ctr); err != nil {
616
// HasContainer checks if a container is present in the state
617
func (s *SQLiteState) HasContainer(id string) (bool, error) {
619
return false, define.ErrEmptyID
623
return false, define.ErrDBClosed
626
row := s.conn.QueryRow("SELECT 1 FROM ContainerConfig WHERE ID=?;", id)
629
if err := row.Scan(&check); err != nil {
630
if errors.Is(err, sql.ErrNoRows) {
633
return false, fmt.Errorf("looking up container %s in database: %w", id, err)
634
} else if check != 1 {
635
return false, fmt.Errorf("check digit for container %s lookup incorrect: %w", id, define.ErrInternal)
641
// AddContainer adds a container to the state
642
// The container being added cannot belong to a pod
643
func (s *SQLiteState) AddContainer(ctr *Container) error {
645
return define.ErrDBClosed
649
return define.ErrCtrRemoved
652
if ctr.config.Pod != "" {
653
return fmt.Errorf("cannot add a container that belongs to a pod with AddContainer - use AddContainerToPod: %w", define.ErrInvalidArg)
656
return s.addContainer(ctr)
659
// RemoveContainer removes a container from the state
660
// Only removes containers not in pods - for containers that are a member of a
661
// pod, use RemoveContainerFromPod
662
func (s *SQLiteState) RemoveContainer(ctr *Container) error {
664
return define.ErrDBClosed
667
if ctr.config.Pod != "" {
668
return fmt.Errorf("container %s is part of a pod, use RemoveContainerFromPod instead: %w", ctr.ID(), define.ErrPodExists)
671
return s.removeContainer(ctr)
674
// UpdateContainer updates a container's state from the database
675
func (s *SQLiteState) UpdateContainer(ctr *Container) error {
677
return define.ErrDBClosed
681
return define.ErrCtrRemoved
684
row := s.conn.QueryRow("SELECT JSON FROM ContainerState WHERE ID=?;", ctr.ID())
687
if err := row.Scan(&rawJSON); err != nil {
688
if errors.Is(err, sql.ErrNoRows) {
689
// Container was removed
691
return fmt.Errorf("no container with ID %s found in database: %w", ctr.ID(), define.ErrNoSuchCtr)
695
newState := new(ContainerState)
696
if err := json.Unmarshal([]byte(rawJSON), newState); err != nil {
697
return fmt.Errorf("unmarshalling container %s state JSON: %w", ctr.ID(), err)
705
// SaveContainer saves a container's current state in the database
706
func (s *SQLiteState) SaveContainer(ctr *Container) (defErr error) {
708
return define.ErrDBClosed
712
return define.ErrCtrRemoved
715
stateJSON, err := json.Marshal(ctr.state)
717
return fmt.Errorf("marshalling container %s state JSON: %w", ctr.ID(), err)
720
tx, err := s.conn.Begin()
722
return fmt.Errorf("beginning container %s save transaction: %w", ctr.ID(), err)
726
if err := tx.Rollback(); err != nil {
727
logrus.Errorf("Rolling back transaction to save container %s state: %v", ctr.ID(), err)
732
result, err := tx.Exec("UPDATE ContainerState SET JSON=? WHERE ID=?;", stateJSON, ctr.ID())
734
return fmt.Errorf("writing container %s state: %w", ctr.ID(), err)
736
rows, err := result.RowsAffected()
738
return fmt.Errorf("retrieving container %s save rows affected: %w", ctr.ID(), err)
742
return define.ErrNoSuchCtr
745
if err := tx.Commit(); err != nil {
746
return fmt.Errorf("committing container %s state: %w", ctr.ID(), err)
752
// ContainerInUse checks if other containers depend on the given container
753
// It returns a slice of the IDs of the containers depending on the given
754
// container. If the slice is empty, no containers depend on the given container
755
func (s *SQLiteState) ContainerInUse(ctr *Container) ([]string, error) {
757
return nil, define.ErrDBClosed
761
return nil, define.ErrCtrRemoved
764
rows, err := s.conn.Query("SELECT ID FROM ContainerDependency WHERE DependencyID=?;", ctr.ID())
766
return nil, fmt.Errorf("retrieving containers that depend on container %s: %w", ctr.ID(), err)
773
if err := rows.Scan(&dep); err != nil {
774
return nil, fmt.Errorf("reading containers that depend on %s: %w", ctr.ID(), err)
776
deps = append(deps, dep)
778
if err := rows.Err(); err != nil {
785
// AllContainers retrieves all the containers in the database
786
// If `loadState` is set, the containers' state will be loaded as well.
787
func (s *SQLiteState) AllContainers(loadState bool) ([]*Container, error) {
789
return nil, define.ErrDBClosed
792
ctrs := []*Container{}
795
rows, err := s.conn.Query("SELECT ContainerConfig.JSON, ContainerState.JSON AS StateJSON FROM ContainerConfig INNER JOIN ContainerState ON ContainerConfig.ID = ContainerState.ID;")
797
return nil, fmt.Errorf("retrieving all containers from database: %w", err)
802
var configJSON, stateJSON string
803
if err := rows.Scan(&configJSON, &stateJSON); err != nil {
804
return nil, fmt.Errorf("scanning container from database: %w", err)
807
ctr := new(Container)
808
ctr.config = new(ContainerConfig)
809
ctr.state = new(ContainerState)
810
ctr.runtime = s.runtime
812
if err := json.Unmarshal([]byte(configJSON), ctr.config); err != nil {
813
return nil, fmt.Errorf("unmarshalling container config: %w", err)
815
if err := json.Unmarshal([]byte(stateJSON), ctr.state); err != nil {
816
return nil, fmt.Errorf("unmarshalling container %s state: %w", ctr.ID(), err)
819
ctrs = append(ctrs, ctr)
821
if err := rows.Err(); err != nil {
825
rows, err := s.conn.Query("SELECT JSON FROM ContainerConfig;")
827
return nil, fmt.Errorf("retrieving all containers from database: %w", err)
833
if err := rows.Scan(&rawJSON); err != nil {
834
return nil, fmt.Errorf("scanning container from database: %w", err)
837
ctr := new(Container)
838
ctr.config = new(ContainerConfig)
839
ctr.state = new(ContainerState)
840
ctr.runtime = s.runtime
842
if err := json.Unmarshal([]byte(rawJSON), ctr.config); err != nil {
843
return nil, fmt.Errorf("unmarshalling container config: %w", err)
846
ctrs = append(ctrs, ctr)
848
if err := rows.Err(); err != nil {
853
for _, ctr := range ctrs {
854
if err := finalizeCtrSqlite(ctr); err != nil {
862
// GetNetworks returns the networks this container is a part of.
863
func (s *SQLiteState) GetNetworks(ctr *Container) (map[string]types.PerNetworkOptions, error) {
865
return nil, define.ErrDBClosed
869
return nil, define.ErrCtrRemoved
872
// if the network mode is not bridge return no networks
873
if !ctr.config.NetMode.IsBridge() {
877
cfg, err := s.getCtrConfig(ctr.ID())
879
if errors.Is(err, define.ErrNoSuchCtr) {
885
return cfg.Networks, nil
888
// NetworkConnect adds the given container to the given network. If aliases are
889
// specified, those will be added to the given network.
890
func (s *SQLiteState) NetworkConnect(ctr *Container, network string, opts types.PerNetworkOptions) error {
891
return s.networkModify(ctr, network, opts, true, false)
894
// NetworkModify will allow you to set new options on an existing connected network
895
func (s *SQLiteState) NetworkModify(ctr *Container, network string, opts types.PerNetworkOptions) error {
896
return s.networkModify(ctr, network, opts, false, false)
899
// NetworkDisconnect disconnects the container from the given network, also
900
// removing any aliases in the network.
901
func (s *SQLiteState) NetworkDisconnect(ctr *Container, network string) error {
902
return s.networkModify(ctr, network, types.PerNetworkOptions{}, false, true)
905
// GetContainerConfig returns a container config from the database by full ID
906
func (s *SQLiteState) GetContainerConfig(id string) (*ContainerConfig, error) {
908
return nil, define.ErrEmptyID
912
return nil, define.ErrDBClosed
915
return s.getCtrConfig(id)
918
// AddContainerExitCode adds the exit code for the specified container to the database.
919
func (s *SQLiteState) AddContainerExitCode(id string, exitCode int32) (defErr error) {
921
return define.ErrEmptyID
925
return define.ErrDBClosed
928
tx, err := s.conn.Begin()
930
return fmt.Errorf("beginning transaction to add exit code: %w", err)
934
if err := tx.Rollback(); err != nil {
935
logrus.Errorf("Rolling back transaction to add exit code: %v", err)
940
if _, err := tx.Exec("INSERT OR REPLACE INTO ContainerExitCode VALUES (?, ?, ?);", id, time.Now().Unix(), exitCode); err != nil {
941
return fmt.Errorf("adding container %s exit code %d: %w", id, exitCode, err)
944
if err := tx.Commit(); err != nil {
945
return fmt.Errorf("committing transaction to add exit code: %w", err)
951
// GetContainerExitCode returns the exit code for the specified container.
952
func (s *SQLiteState) GetContainerExitCode(id string) (int32, error) {
954
return -1, define.ErrEmptyID
958
return -1, define.ErrDBClosed
961
row := s.conn.QueryRow("SELECT ExitCode FROM ContainerExitCode WHERE ID=?;", id)
962
var exitCode int32 = -1
963
if err := row.Scan(&exitCode); err != nil {
964
if errors.Is(err, sql.ErrNoRows) {
965
return -1, fmt.Errorf("getting exit code of container %s from DB: %w", id, define.ErrNoSuchExitCode)
967
return -1, fmt.Errorf("scanning exit code of container %s: %w", id, err)
973
// GetContainerExitCodeTimeStamp returns the time stamp when the exit code of
974
// the specified container was added to the database.
975
func (s *SQLiteState) GetContainerExitCodeTimeStamp(id string) (*time.Time, error) {
977
return nil, define.ErrEmptyID
981
return nil, define.ErrDBClosed
984
row := s.conn.QueryRow("SELECT Timestamp FROM ContainerExitCode WHERE ID=?;", id)
987
if err := row.Scan(×tamp); err != nil {
988
if errors.Is(err, sql.ErrNoRows) {
989
return nil, fmt.Errorf("getting timestamp for exit code of container %s from DB: %w", id, define.ErrNoSuchExitCode)
991
return nil, fmt.Errorf("scanning exit timestamp of container %s: %w", id, err)
994
result := time.Unix(timestamp, 0)
999
// PruneExitCodes removes exit codes older than 5 minutes unless the associated
1000
// container still exists.
1001
func (s *SQLiteState) PruneContainerExitCodes() (defErr error) {
1003
return define.ErrDBClosed
1006
fiveMinsAgo := time.Now().Add(-5 * time.Minute).Unix()
1008
tx, err := s.conn.Begin()
1010
return fmt.Errorf("beginning transaction to remove old timestamps: %w", err)
1014
if err := tx.Rollback(); err != nil {
1015
logrus.Errorf("Rolling back transaction to remove old timestamps: %v", err)
1020
if _, err := tx.Exec("DELETE FROM ContainerExitCode WHERE (Timestamp <= ?) AND (ID NOT IN (SELECT ID FROM ContainerConfig))", fiveMinsAgo); err != nil {
1021
return fmt.Errorf("removing exit codes with timestamps older than 5 minutes: %w", err)
1024
if err := tx.Commit(); err != nil {
1025
return fmt.Errorf("committing transaction to remove old timestamps: %w", err)
1031
// AddExecSession adds an exec session to the state.
1032
func (s *SQLiteState) AddExecSession(ctr *Container, session *ExecSession) (defErr error) {
1034
return define.ErrDBClosed
1038
return define.ErrCtrRemoved
1041
tx, err := s.conn.Begin()
1043
return fmt.Errorf("beginning container %s exec session %s add transaction: %w", ctr.ID(), session.Id, err)
1047
if err := tx.Rollback(); err != nil {
1048
logrus.Errorf("Rolling back transaction to add container %s exec session %s: %v", ctr.ID(), session.Id, err)
1053
if _, err := tx.Exec("INSERT INTO ContainerExecSession VALUES (?, ?);", session.Id, ctr.ID()); err != nil {
1054
return fmt.Errorf("adding container %s exec session %s to database: %w", ctr.ID(), session.Id, err)
1057
if err := tx.Commit(); err != nil {
1058
return fmt.Errorf("committing container %s exec session %s addition: %w", ctr.ID(), session.Id, err)
1064
// GetExecSession returns the ID of the container an exec session is associated
1066
func (s *SQLiteState) GetExecSession(id string) (string, error) {
1068
return "", define.ErrDBClosed
1072
return "", define.ErrEmptyID
1075
row := s.conn.QueryRow("SELECT ContainerID FROM ContainerExecSession WHERE ID=?;", id)
1078
if err := row.Scan(&ctrID); err != nil {
1079
if errors.Is(err, sql.ErrNoRows) {
1080
return "", fmt.Errorf("no exec session with ID %s found: %w", id, define.ErrNoSuchExecSession)
1082
return "", fmt.Errorf("retrieving exec session %s from database: %w", id, err)
1088
// RemoveExecSession removes references to the given exec session in the
1090
func (s *SQLiteState) RemoveExecSession(session *ExecSession) (defErr error) {
1092
return define.ErrDBClosed
1095
tx, err := s.conn.Begin()
1097
return fmt.Errorf("beginning container %s exec session %s remove transaction: %w", session.ContainerId, session.Id, err)
1101
if err := tx.Rollback(); err != nil {
1102
logrus.Errorf("Rolling back transaction to remove container %s exec session %s: %v", session.ContainerId, session.Id, err)
1107
result, err := tx.Exec("DELETE FROM ContainerExecSession WHERE ID=?;", session.Id)
1109
return fmt.Errorf("removing container %s exec session %s from database: %w", session.ContainerId, session.Id, err)
1111
rows, err := result.RowsAffected()
1113
return fmt.Errorf("retrieving container %s exec session %s removal rows modified: %w", session.ContainerId, session.Id, err)
1116
return define.ErrNoSuchExecSession
1119
if err := tx.Commit(); err != nil {
1120
return fmt.Errorf("committing container %s exec session %s removal: %w", session.ContainerId, session.Id, err)
1126
// GetContainerExecSessions retrieves the IDs of all exec sessions running in a
1127
// container that the database is aware of (IE, were added via AddExecSession).
1128
func (s *SQLiteState) GetContainerExecSessions(ctr *Container) ([]string, error) {
1130
return nil, define.ErrDBClosed
1134
return nil, define.ErrCtrRemoved
1137
rows, err := s.conn.Query("SELECT ID FROM ContainerExecSession WHERE ContainerID=?;", ctr.ID())
1139
return nil, fmt.Errorf("querying container %s exec sessions: %w", ctr.ID(), err)
1143
var sessions []string
1146
if err := rows.Scan(&session); err != nil {
1147
return nil, fmt.Errorf("scanning container %s exec sessions row: %w", ctr.ID(), err)
1149
sessions = append(sessions, session)
1151
if err := rows.Err(); err != nil {
1155
return sessions, nil
1158
// RemoveContainerExecSessions removes all exec sessions attached to a given
1160
func (s *SQLiteState) RemoveContainerExecSessions(ctr *Container) (defErr error) {
1162
return define.ErrDBClosed
1166
return define.ErrCtrRemoved
1169
tx, err := s.conn.Begin()
1171
return fmt.Errorf("beginning container %s exec session removal transaction: %w", ctr.ID(), err)
1175
if err := tx.Rollback(); err != nil {
1176
logrus.Errorf("Rolling back transaction to remove container %s exec sessions: %v", ctr.ID(), err)
1181
if _, err := tx.Exec("DELETE FROM ContainerExecSession WHERE ContainerID=?;", ctr.ID()); err != nil {
1182
return fmt.Errorf("removing container %s exec sessions from database: %w", ctr.ID(), err)
1185
if err := tx.Commit(); err != nil {
1186
return fmt.Errorf("committing container %s exec session removal: %w", ctr.ID(), err)
1192
// RewriteContainerConfig rewrites a container's configuration.
1193
// DO NOT USE TO: Change container dependencies, change pod membership, change
1195
// WARNING: This function is DANGEROUS. Do not use without reading the full
1196
// comment on this function in state.go.
1197
// TODO: Once BoltDB is removed, this can be combined with SafeRewriteContainerConfig.
1198
func (s *SQLiteState) RewriteContainerConfig(ctr *Container, newCfg *ContainerConfig) error {
1200
return define.ErrDBClosed
1204
return define.ErrCtrRemoved
1207
return s.rewriteContainerConfig(ctr, newCfg)
1210
// SafeRewriteContainerConfig rewrites a container's configuration in a more
1211
// limited fashion than RewriteContainerConfig. It is marked as safe to use
1212
// under most circumstances, unlike RewriteContainerConfig.
1213
// DO NOT USE TO: Change container dependencies, change pod membership, change
1214
// locks, change container ID.
1215
// TODO: Once BoltDB is removed, this can be combined with RewriteContainerConfig.
1216
func (s *SQLiteState) SafeRewriteContainerConfig(ctr *Container, oldName, newName string, newCfg *ContainerConfig) error {
1218
return define.ErrDBClosed
1222
return define.ErrCtrRemoved
1225
if newName != "" && newCfg.Name != newName {
1226
return fmt.Errorf("new name %s for container %s must match name in given container config: %w", newName, ctr.ID(), define.ErrInvalidArg)
1228
if newName != "" && oldName == "" {
1229
return fmt.Errorf("must provide old name for container if a new name is given: %w", define.ErrInvalidArg)
1232
return s.rewriteContainerConfig(ctr, newCfg)
1235
// RewritePodConfig rewrites a pod's configuration.
1236
// WARNING: This function is DANGEROUS. Do not use without reading the full
1237
// comment on this function in state.go.
1238
func (s *SQLiteState) RewritePodConfig(pod *Pod, newCfg *PodConfig) (defErr error) {
1240
return define.ErrDBClosed
1244
return define.ErrPodRemoved
1247
json, err := json.Marshal(newCfg)
1249
return fmt.Errorf("error marshalling pod %s config JSON: %w", pod.ID(), err)
1252
tx, err := s.conn.Begin()
1254
return fmt.Errorf("beginning transaction to rewrite pod %s config: %w", pod.ID(), err)
1258
if err := tx.Rollback(); err != nil {
1259
logrus.Errorf("Rolling back transaction to rewrite pod %s config: %v", pod.ID(), err)
1264
results, err := tx.Exec("UPDATE PodConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, pod.ID())
1266
return fmt.Errorf("updating pod config table with new configuration for pod %s: %w", pod.ID(), err)
1268
rows, err := results.RowsAffected()
1270
return fmt.Errorf("retrieving pod %s config rewrite rows affected: %w", pod.ID(), err)
1274
return fmt.Errorf("no pod with ID %s found in DB: %w", pod.ID(), define.ErrNoSuchPod)
1277
if err := tx.Commit(); err != nil {
1278
return fmt.Errorf("committing transaction to rewrite pod %s config: %w", pod.ID(), err)
1284
// RewriteVolumeConfig rewrites a volume's configuration.
1285
// WARNING: This function is DANGEROUS. Do not use without reading the full
1286
// comment on this function in state.go.
1287
func (s *SQLiteState) RewriteVolumeConfig(volume *Volume, newCfg *VolumeConfig) (defErr error) {
1289
return define.ErrDBClosed
1293
return define.ErrVolumeRemoved
1296
json, err := json.Marshal(newCfg)
1298
return fmt.Errorf("error marshalling volume %s new config JSON: %w", volume.Name(), err)
1301
tx, err := s.conn.Begin()
1303
return fmt.Errorf("beginning transaction to rewrite volume %s config: %w", volume.Name(), err)
1307
if err := tx.Rollback(); err != nil {
1308
logrus.Errorf("Rolling back transaction to rewrite volume %s config: %v", volume.Name(), err)
1313
results, err := tx.Exec("UPDATE VolumeConfig SET Name=?, JSON=? WHERE ID=?;", newCfg.Name, json, volume.Name())
1315
return fmt.Errorf("updating volume config table with new configuration for volume %s: %w", volume.Name(), err)
1317
rows, err := results.RowsAffected()
1319
return fmt.Errorf("retrieving volume %s config rewrite rows affected: %w", volume.Name(), err)
1322
volume.valid = false
1323
return fmt.Errorf("no volume with name %q found in DB: %w", volume.Name(), define.ErrNoSuchVolume)
1326
if err := tx.Commit(); err != nil {
1327
return fmt.Errorf("committing transaction to rewrite volume %s config: %w", volume.Name(), err)
1333
// Pod retrieves a pod given its full ID
1334
func (s *SQLiteState) Pod(id string) (*Pod, error) {
1336
return nil, define.ErrEmptyID
1340
return nil, define.ErrDBClosed
1343
row := s.conn.QueryRow("SELECT JSON FROM PodConfig WHERE ID=?;", id)
1345
if err := row.Scan(&rawJSON); err != nil {
1346
if errors.Is(err, sql.ErrNoRows) {
1347
return nil, define.ErrNoSuchPod
1349
return nil, fmt.Errorf("retrieving pod %s config from DB: %w", id, err)
1352
ctrCfg := new(ContainerConfig)
1353
if err := json.Unmarshal([]byte(rawJSON), ctrCfg); err != nil {
1354
return nil, fmt.Errorf("unmarshalling container %s config: %w", id, err)
1357
return s.createPod(rawJSON)
1360
// LookupPod retrieves a pod from a full or unique partial ID, or a name.
1361
func (s *SQLiteState) LookupPod(idOrName string) (*Pod, error) {
1363
return nil, define.ErrEmptyID
1367
return nil, define.ErrDBClosed
1370
rows, err := s.conn.Query("SELECT JSON, Name FROM PodConfig WHERE PodConfig.Name=? OR (PodConfig.ID LIKE ?);", idOrName, idOrName+"%")
1372
return nil, fmt.Errorf("looking up pod %q in database: %w", idOrName, err)
1377
rawJSON, name string
1382
if err := rows.Scan(&rawJSON, &name); err != nil {
1383
return nil, fmt.Errorf("error retrieving pod %q ID from database: %w", idOrName, err)
1385
if name == idOrName {
1391
if err := rows.Err(); err != nil {
1396
return nil, fmt.Errorf("no pod with name or ID %s found: %w", idOrName, define.ErrNoSuchPod)
1397
} else if resCount > 1 {
1398
return nil, fmt.Errorf("more than one result for pod %q: %w", idOrName, define.ErrCtrExists)
1402
return s.createPod(rawJSON)
1405
// HasPod checks if a pod with the given ID exists in the state
1406
func (s *SQLiteState) HasPod(id string) (bool, error) {
1408
return false, define.ErrEmptyID
1412
return false, define.ErrDBClosed
1415
row := s.conn.QueryRow("SELECT 1 FROM PodConfig WHERE ID=?;", id)
1418
if err := row.Scan(&check); err != nil {
1419
if errors.Is(err, sql.ErrNoRows) {
1422
return false, fmt.Errorf("looking up pod %s in database: %w", id, err)
1423
} else if check != 1 {
1424
return false, fmt.Errorf("check digit for pod %s lookup incorrect: %w", id, define.ErrInternal)
1430
// PodHasContainer checks if the given pod has a container with the given ID
1431
func (s *SQLiteState) PodHasContainer(pod *Pod, id string) (bool, error) {
1433
return false, define.ErrEmptyID
1437
return false, define.ErrDBClosed
1441
return false, define.ErrPodRemoved
1445
row := s.conn.QueryRow("SELECT 1 FROM ContainerConfig WHERE ID=? AND PodID=?;", id, pod.ID())
1446
if err := row.Scan(&check); err != nil {
1447
if errors.Is(err, sql.ErrNoRows) {
1450
return false, fmt.Errorf("checking if pod %s has container %s in database: %w", pod.ID(), id, err)
1451
} else if check != 1 {
1452
return false, fmt.Errorf("check digit for pod %s lookup incorrect: %w", id, define.ErrInternal)
1458
// PodContainersByID returns the IDs of all containers present in the given pod
1459
func (s *SQLiteState) PodContainersByID(pod *Pod) ([]string, error) {
1461
return nil, define.ErrDBClosed
1465
return nil, define.ErrPodRemoved
1468
rows, err := s.conn.Query("SELECT ID FROM ContainerConfig WHERE PodID=?;", pod.ID())
1470
return nil, fmt.Errorf("retrieving container IDs of pod %s from database: %w", pod.ID(), err)
1477
if err := rows.Scan(&id); err != nil {
1478
return nil, fmt.Errorf("scanning container from database: %w", err)
1481
ids = append(ids, id)
1483
if err := rows.Err(); err != nil {
1490
// PodContainers returns all the containers present in the given pod
1491
func (s *SQLiteState) PodContainers(pod *Pod) ([]*Container, error) {
1493
return nil, define.ErrDBClosed
1497
return nil, define.ErrPodRemoved
1500
rows, err := s.conn.Query("SELECT JSON FROM ContainerConfig WHERE PodID=?;", pod.ID())
1502
return nil, fmt.Errorf("retrieving containers of pod %s from database: %w", pod.ID(), err)
1506
var ctrs []*Container
1509
if err := rows.Scan(&rawJSON); err != nil {
1510
return nil, fmt.Errorf("scanning container from database: %w", err)
1513
ctr := new(Container)
1514
ctr.config = new(ContainerConfig)
1515
ctr.state = new(ContainerState)
1516
ctr.runtime = s.runtime
1518
if err := json.Unmarshal([]byte(rawJSON), ctr.config); err != nil {
1519
return nil, fmt.Errorf("unmarshalling container config: %w", err)
1522
ctrs = append(ctrs, ctr)
1524
if err := rows.Err(); err != nil {
1528
for _, ctr := range ctrs {
1529
if err := finalizeCtrSqlite(ctr); err != nil {
1537
// AddPod adds the given pod to the state.
1538
func (s *SQLiteState) AddPod(pod *Pod) (defErr error) {
1540
return define.ErrDBClosed
1544
return define.ErrPodRemoved
1547
infraID := sql.NullString{}
1548
if pod.state.InfraContainerID != "" {
1549
if err := infraID.Scan(pod.state.InfraContainerID); err != nil {
1550
return fmt.Errorf("scanning infra container ID %q: %w", pod.state.InfraContainerID, err)
1554
configJSON, err := json.Marshal(pod.config)
1556
return fmt.Errorf("marshalling pod config json: %w", err)
1559
stateJSON, err := json.Marshal(pod.state)
1561
return fmt.Errorf("marshalling pod state json: %w", err)
1564
tx, err := s.conn.Begin()
1566
return fmt.Errorf("beginning pod create transaction: %w", err)
1570
if err := tx.Rollback(); err != nil {
1571
logrus.Errorf("Rolling back transaction to create pod: %v", err)
1576
// TODO: explore whether there's a more idiomatic way to do error checks for the name.
1577
// There is a sqlite3.ErrConstraintUnique error but I (vrothberg) couldn't find a way
1578
// to work with the returned errors yet.
1580
row := tx.QueryRow("SELECT 1 FROM PodConfig WHERE Name=?;", pod.Name())
1581
if err := row.Scan(&check); err != nil {
1582
if !errors.Is(err, sql.ErrNoRows) {
1583
return fmt.Errorf("checking if pod name %s exists in database: %w", pod.Name(), err)
1585
} else if check != 0 {
1586
return fmt.Errorf("name %q is in use: %w", pod.Name(), define.ErrPodExists)
1589
if _, err := tx.Exec("INSERT INTO IDNamespace VALUES (?);", pod.ID()); err != nil {
1590
return fmt.Errorf("adding pod id to database: %w", err)
1592
if _, err := tx.Exec("INSERT INTO PodConfig VALUES (?, ?, ?);", pod.ID(), pod.Name(), configJSON); err != nil {
1593
return fmt.Errorf("adding pod config to database: %w", err)
1595
if _, err := tx.Exec("INSERT INTO PodState VALUES (?, ?, ?);", pod.ID(), infraID, stateJSON); err != nil {
1596
return fmt.Errorf("adding pod state to database: %w", err)
1599
if err := tx.Commit(); err != nil {
1600
return fmt.Errorf("committing transaction: %w", err)
1606
// RemovePod removes the given pod from the state.
1607
// Only empty pods can be removed.
1608
func (s *SQLiteState) RemovePod(pod *Pod) (defErr error) {
1610
return define.ErrDBClosed
1614
return define.ErrPodRemoved
1617
tx, err := s.conn.Begin()
1619
return fmt.Errorf("beginning pod %s removal transaction: %w", pod.ID(), err)
1623
if err := tx.Rollback(); err != nil {
1624
logrus.Errorf("Rolling back transaction to remove pod %s: %v", pod.ID(), err)
1630
row := tx.QueryRow("SELECT 1 FROM ContainerConfig WHERE PodID=? AND ID!=?;", pod.ID(), pod.state.InfraContainerID)
1631
if err := row.Scan(&check); err != nil {
1632
if !errors.Is(err, sql.ErrNoRows) {
1633
return fmt.Errorf("checking if pod %s has containers in database: %w", pod.ID(), err)
1635
} else if check != 0 {
1636
return fmt.Errorf("pod %s is not empty: %w", pod.ID(), define.ErrCtrExists)
1639
checkResult := func(result sql.Result) error {
1640
rows, err := result.RowsAffected()
1642
return fmt.Errorf("retrieving pod %s delete rows affected: %w", pod.ID(), err)
1646
return define.ErrNoSuchPod
1651
result, err := tx.Exec("DELETE FROM IDNamespace WHERE ID=?;", pod.ID())
1653
return fmt.Errorf("removing pod %s id from database: %w", pod.ID(), err)
1655
if err := checkResult(result); err != nil {
1659
result, err = tx.Exec("DELETE FROM PodConfig WHERE ID=?;", pod.ID())
1661
return fmt.Errorf("removing pod %s config from database: %w", pod.ID(), err)
1663
if err := checkResult(result); err != nil {
1667
result, err = tx.Exec("DELETE FROM PodState WHERE ID=?;", pod.ID())
1669
return fmt.Errorf("removing pod %s state from database: %w", pod.ID(), err)
1671
if err := checkResult(result); err != nil {
1675
if err := tx.Commit(); err != nil {
1676
return fmt.Errorf("committing pod %s removal transaction: %w", pod.ID(), err)
1682
// RemovePodContainers removes all containers in a pod.
1683
func (s *SQLiteState) RemovePodContainers(pod *Pod) (defErr error) {
1685
return define.ErrDBClosed
1689
return define.ErrPodRemoved
1692
tx, err := s.conn.Begin()
1694
return fmt.Errorf("beginning removal transaction for containers of pod %s: %w", pod.ID(), err)
1698
if err := tx.Rollback(); err != nil {
1699
logrus.Errorf("Rolling back transaction to remove containers of pod %s: %v", pod.ID(), err)
1704
rows, err := tx.Query("SELECT ID FROM ContainerConfig WHERE PodID=?;", pod.ID())
1706
return fmt.Errorf("retrieving container IDs of pod %s from database: %w", pod.ID(), err)
1712
if err := rows.Scan(&id); err != nil {
1713
return fmt.Errorf("scanning container from database: %w", err)
1716
if err := s.removeContainerWithTx(id, tx); err != nil {
1720
if err := rows.Err(); err != nil {
1724
if err := tx.Commit(); err != nil {
1725
return fmt.Errorf("committing pod containers %s removal transaction: %w", pod.ID(), err)
1731
// AddContainerToPod adds the given container to an existing pod
1732
// The container will be added to the state and the pod
1733
func (s *SQLiteState) AddContainerToPod(pod *Pod, ctr *Container) error {
1735
return define.ErrDBClosed
1739
return define.ErrPodRemoved
1743
return define.ErrCtrRemoved
1746
if ctr.config.Pod != pod.ID() {
1747
return fmt.Errorf("container %s is not part of pod %s: %w", ctr.ID(), pod.ID(), define.ErrNoSuchCtr)
1750
return s.addContainer(ctr)
1753
// RemoveContainerFromPod removes a container from an existing pod
1754
// The container will also be removed from the state
1755
func (s *SQLiteState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
1757
return define.ErrDBClosed
1761
return define.ErrPodRemoved
1764
if ctr.config.Pod == "" {
1765
return fmt.Errorf("container %s is not part of a pod, use RemoveContainer instead: %w", ctr.ID(), define.ErrNoSuchPod)
1768
if ctr.config.Pod != pod.ID() {
1769
return fmt.Errorf("container %s is not part of pod %s: %w", ctr.ID(), pod.ID(), define.ErrInvalidArg)
1772
return s.removeContainer(ctr)
1775
// UpdatePod updates a pod's state from the database.
1776
func (s *SQLiteState) UpdatePod(pod *Pod) error {
1778
return define.ErrDBClosed
1782
return define.ErrPodRemoved
1785
row := s.conn.QueryRow("SELECT JSON FROM PodState WHERE ID=?;", pod.ID())
1788
if err := row.Scan(&rawJSON); err != nil {
1789
if errors.Is(err, sql.ErrNoRows) {
1792
return fmt.Errorf("no pod with ID %s found in database: %w", pod.ID(), define.ErrNoSuchPod)
1796
newState := new(podState)
1797
if err := json.Unmarshal([]byte(rawJSON), newState); err != nil {
1798
return fmt.Errorf("unmarshalling pod %s state JSON: %w", pod.ID(), err)
1801
pod.state = newState
1806
// SavePod saves a pod's state to the database.
1807
func (s *SQLiteState) SavePod(pod *Pod) (defErr error) {
1809
return define.ErrDBClosed
1813
return define.ErrPodRemoved
1816
stateJSON, err := json.Marshal(pod.state)
1818
return fmt.Errorf("marshalling pod %s state JSON: %w", pod.ID(), err)
1821
tx, err := s.conn.Begin()
1823
return fmt.Errorf("beginning pod %s save transaction: %w", pod.ID(), err)
1827
if err := tx.Rollback(); err != nil {
1828
logrus.Errorf("Rolling back transaction to save pod %s state: %v", pod.ID(), err)
1833
result, err := tx.Exec("UPDATE PodState SET JSON=? WHERE ID=?;", stateJSON, pod.ID())
1835
return fmt.Errorf("writing pod %s state: %w", pod.ID(), err)
1837
rows, err := result.RowsAffected()
1839
return fmt.Errorf("retrieving pod %s save rows affected: %w", pod.ID(), err)
1843
return define.ErrNoSuchPod
1846
if err := tx.Commit(); err != nil {
1847
return fmt.Errorf("committing pod %s state: %w", pod.ID(), err)
1853
// AllPods returns all pods present in the state.
1854
func (s *SQLiteState) AllPods() ([]*Pod, error) {
1856
return nil, define.ErrDBClosed
1860
rows, err := s.conn.Query("SELECT JSON FROM PodConfig;")
1862
return nil, fmt.Errorf("retrieving all pods from database: %w", err)
1868
if err := rows.Scan(&rawJSON); err != nil {
1869
return nil, fmt.Errorf("scanning pod from database: %w", err)
1872
pod, err := s.createPod(rawJSON)
1877
pods = append(pods, pod)
1879
if err := rows.Err(); err != nil {
1886
// AddVolume adds the given volume to the state. It also adds ctrDepID to
1887
// the sub bucket holding the container dependencies that this volume has
1888
func (s *SQLiteState) AddVolume(volume *Volume) (defErr error) {
1890
return define.ErrDBClosed
1894
return define.ErrVolumeRemoved
1897
cfgJSON, err := json.Marshal(volume.config)
1899
return fmt.Errorf("marshalling volume %s configuration json: %w", volume.Name(), err)
1902
volState := volume.state
1903
if volState == nil {
1904
volState = new(VolumeState)
1907
stateJSON, err := json.Marshal(volState)
1909
return fmt.Errorf("marshalling volume %s state json: %w", volume.Name(), err)
1912
storageID := sql.NullString{}
1913
if volume.config.StorageID != "" {
1914
storageID.Valid = true
1915
storageID.String = volume.config.StorageID
1918
tx, err := s.conn.Begin()
1920
return fmt.Errorf("beginning volume create transaction: %w", err)
1924
if err := tx.Rollback(); err != nil {
1925
logrus.Errorf("Rolling back transaction to create volume: %v", err)
1930
// TODO: There has to be a better way of doing this
1932
row := tx.QueryRow("SELECT 1 FROM VolumeConfig WHERE Name=?;", volume.Name())
1933
if err := row.Scan(&check); err != nil {
1934
if !errors.Is(err, sql.ErrNoRows) {
1935
return fmt.Errorf("checking if volume name %s exists in database: %w", volume.Name(), err)
1937
} else if check != 0 {
1938
return fmt.Errorf("name %q is in use: %w", volume.Name(), define.ErrVolumeExists)
1941
if _, err := tx.Exec("INSERT INTO VolumeConfig VALUES (?, ?, ?);", volume.Name(), storageID, cfgJSON); err != nil {
1942
return fmt.Errorf("adding volume %s config to database: %w", volume.Name(), err)
1945
if _, err := tx.Exec("INSERT INTO VolumeState VALUES (?, ?);", volume.Name(), stateJSON); err != nil {
1946
return fmt.Errorf("adding volume %s state to database: %w", volume.Name(), err)
1949
if err := tx.Commit(); err != nil {
1950
return fmt.Errorf("committing transaction: %w", err)
1956
// RemoveVolume removes the given volume from the state
1957
func (s *SQLiteState) RemoveVolume(volume *Volume) (defErr error) {
1959
return define.ErrDBClosed
1962
tx, err := s.conn.Begin()
1964
return fmt.Errorf("beginning volume %s removal transaction: %w", volume.Name(), err)
1968
if err := tx.Rollback(); err != nil {
1969
logrus.Errorf("Rolling back transaction to remove volume %s: %v", volume.Name(), err)
1974
rows, err := tx.Query("SELECT ContainerID FROM ContainerVolume WHERE VolumeName=?;", volume.Name())
1976
return fmt.Errorf("querying for containers using volume %s: %w", volume.Name(), err)
1983
if err := rows.Scan(&ctr); err != nil {
1984
return fmt.Errorf("error scanning row for containers using volume %s: %w", volume.Name(), err)
1986
ctrs = append(ctrs, ctr)
1988
if err := rows.Err(); err != nil {
1992
return fmt.Errorf("volume %s is in use by containers %s: %w", volume.Name(), strings.Join(ctrs, ","), define.ErrVolumeBeingUsed)
1996
// Need to verify that at least 1 row was deleted from VolumeConfig.
1997
// Otherwise return ErrNoSuchVolume
1999
if _, err := tx.Exec("DELETE FROM VolumeConfig WHERE Name=?;", volume.Name()); err != nil {
2000
return fmt.Errorf("removing volume %s config from DB: %w", volume.Name(), err)
2003
if _, err := tx.Exec("DELETE FROM VolumeState WHERE Name=?;", volume.Name()); err != nil {
2004
return fmt.Errorf("removing volume %s state from DB: %w", volume.Name(), err)
2007
if err := tx.Commit(); err != nil {
2008
return fmt.Errorf("committing transaction to remove volume %s: %w", volume.Name(), err)
2014
// UpdateVolume updates the volume's state from the database.
2015
func (s *SQLiteState) UpdateVolume(volume *Volume) error {
2017
return define.ErrDBClosed
2021
return define.ErrVolumeRemoved
2024
row := s.conn.QueryRow("SELECT JSON FROM VolumeState WHERE Name=?;", volume.Name())
2026
var stateJSON string
2027
if err := row.Scan(&stateJSON); err != nil {
2028
if errors.Is(err, sql.ErrNoRows) {
2029
volume.valid = false
2030
return define.ErrNoSuchVolume
2032
return fmt.Errorf("scanning volume %s state JSON: %w", volume.Name(), err)
2035
newState := new(VolumeState)
2036
if err := json.Unmarshal([]byte(stateJSON), newState); err != nil {
2037
return fmt.Errorf("unmarshalling volume %s state: %w", volume.Name(), err)
2040
volume.state = newState
2045
// SaveVolume saves the volume's state to the database.
2046
func (s *SQLiteState) SaveVolume(volume *Volume) (defErr error) {
2048
return define.ErrDBClosed
2052
return define.ErrVolumeRemoved
2055
stateJSON, err := json.Marshal(volume.state)
2057
return fmt.Errorf("marshalling volume %s state JSON: %w", volume.Name(), err)
2060
tx, err := s.conn.Begin()
2062
return fmt.Errorf("beginning transaction to rewrite volume %s state: %w", volume.Name(), err)
2066
if err := tx.Rollback(); err != nil {
2067
logrus.Errorf("Rolling back transaction to rewrite volume %s state: %v", volume.Name(), err)
2072
results, err := tx.Exec("UPDATE VolumeState SET JSON=? WHERE Name=?;", stateJSON, volume.Name())
2074
return fmt.Errorf("updating volume %s state in DB: %w", volume.Name(), err)
2076
rows, err := results.RowsAffected()
2078
return fmt.Errorf("retrieving volume %s state rewrite rows affected: %w", volume.Name(), err)
2081
volume.valid = false
2082
return define.ErrNoSuchVolume
2085
if err := tx.Commit(); err != nil {
2086
return fmt.Errorf("committing transaction to rewrite volume %s state: %w", volume.Name(), err)
2092
// AllVolumes returns all volumes present in the state.
2093
func (s *SQLiteState) AllVolumes() ([]*Volume, error) {
2095
return nil, define.ErrDBClosed
2098
rows, err := s.conn.Query("SELECT JSON FROM VolumeConfig;")
2100
return nil, fmt.Errorf("querying database for all volumes: %w", err)
2104
var volumes []*Volume
2107
var configJSON string
2108
if err := rows.Scan(&configJSON); err != nil {
2109
return nil, fmt.Errorf("scanning volume config from database: %w", err)
2112
vol.config = new(VolumeConfig)
2113
vol.state = new(VolumeState)
2114
vol.runtime = s.runtime
2116
if err := json.Unmarshal([]byte(configJSON), vol.config); err != nil {
2117
return nil, fmt.Errorf("unmarshalling volume config: %w", err)
2120
if err := finalizeVolumeSqlite(vol); err != nil {
2124
volumes = append(volumes, vol)
2126
if err := rows.Err(); err != nil {
2133
// Volume retrieves a volume from full name.
2134
func (s *SQLiteState) Volume(name string) (*Volume, error) {
2136
return nil, define.ErrEmptyID
2140
return nil, define.ErrDBClosed
2143
row := s.conn.QueryRow("SELECT JSON FROM VolumeConfig WHERE Name=?;", name)
2145
var configJSON string
2147
if err := row.Scan(&configJSON); err != nil {
2148
if errors.Is(err, sql.ErrNoRows) {
2149
return nil, define.ErrNoSuchVolume
2154
vol.config = new(VolumeConfig)
2155
vol.state = new(VolumeState)
2156
vol.runtime = s.runtime
2158
if err := json.Unmarshal([]byte(configJSON), vol.config); err != nil {
2159
return nil, fmt.Errorf("unmarshalling volume %s config JSON: %w", name, err)
2162
if err := finalizeVolumeSqlite(vol); err != nil {
2169
// LookupVolume locates a volume from a unique partial name.
2170
func (s *SQLiteState) LookupVolume(name string) (*Volume, error) {
2172
return nil, define.ErrEmptyID
2176
return nil, define.ErrDBClosed
2179
rows, err := s.conn.Query("SELECT Name, JSON FROM VolumeConfig WHERE Name LIKE ? ORDER BY LENGTH(Name) ASC;", name+"%")
2181
return nil, fmt.Errorf("querying database for volume %s: %w", name, err)
2185
var foundName, configJSON string
2187
if foundName != "" {
2188
return nil, fmt.Errorf("more than one result for volume name %s: %w", name, define.ErrVolumeExists)
2190
if err := rows.Scan(&foundName, &configJSON); err != nil {
2191
return nil, fmt.Errorf("retrieving volume %s config from database: %w", name, err)
2193
if foundName == name {
2197
if err := rows.Err(); err != nil {
2200
if foundName == "" {
2201
return nil, fmt.Errorf("no volume with name %q found: %w", name, define.ErrNoSuchVolume)
2205
vol.config = new(VolumeConfig)
2206
vol.state = new(VolumeState)
2207
vol.runtime = s.runtime
2209
if err := json.Unmarshal([]byte(configJSON), vol.config); err != nil {
2210
return nil, fmt.Errorf("unmarshalling volume %s config JSON: %w", name, err)
2213
if err := finalizeVolumeSqlite(vol); err != nil {
2220
// HasVolume returns true if the given volume exists in the state.
2221
// Otherwise it returns false.
2222
func (s *SQLiteState) HasVolume(name string) (bool, error) {
2224
return false, define.ErrEmptyID
2228
return false, define.ErrDBClosed
2231
row := s.conn.QueryRow("SELECT 1 FROM VolumeConfig WHERE Name=?;", name)
2234
if err := row.Scan(&check); err != nil {
2235
if errors.Is(err, sql.ErrNoRows) {
2238
return false, fmt.Errorf("looking up volume %s in database: %w", name, err)
2241
return false, fmt.Errorf("check digit for volume %s lookup incorrect: %w", name, define.ErrInternal)
2247
// VolumeInUse checks if any container is using the volume.
2248
// It returns a slice of the IDs of the containers using the given
2249
// volume. If the slice is empty, no containers use the given volume.
2250
func (s *SQLiteState) VolumeInUse(volume *Volume) ([]string, error) {
2252
return nil, define.ErrDBClosed
2256
return nil, define.ErrVolumeRemoved
2259
rows, err := s.conn.Query("SELECT ContainerID FROM ContainerVolume WHERE VolumeName=?;", volume.Name())
2261
return nil, fmt.Errorf("querying database for containers using volume %s: %w", volume.Name(), err)
2268
if err := rows.Scan(&ctr); err != nil {
2269
return nil, fmt.Errorf("scanning container ID for container using volume %s: %w", volume.Name(), err)
2271
ctrs = append(ctrs, ctr)
2273
if err := rows.Err(); err != nil {
2280
// ContainerIDIsVolume checks if the given c/storage container ID is used as
2281
// backing storage for a volume.
2282
func (s *SQLiteState) ContainerIDIsVolume(id string) (bool, error) {
2284
return false, define.ErrDBClosed
2287
row := s.conn.QueryRow("SELECT 1 FROM VolumeConfig WHERE StorageID=?;", id)
2289
if err := row.Scan(&checkDigit); err != nil {
2290
if errors.Is(err, sql.ErrNoRows) {
2293
return false, fmt.Errorf("error retrieving volumes using storage ID %s: %w", id, err)
2295
if checkDigit != 1 {
2296
return false, fmt.Errorf("check digit for volumes using storage ID %s was incorrect: %w", id, define.ErrInternal)