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(×tamp); 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 }