Deprecate BoltDB, preventing creation of new databases.

This is one of the breaking changes in Podman 5.0: removing the
ability to create new instances of the old Bolt database. This
does not remove support for the database entirely, as existing
Bolt databases will still be usable, but all new installs will
use SQLite after this point - if Bolt is forced by config, we'll
just error.

We don't have plans to outright remove the Bolt code. If that
were to happen, it'd be Podman 6.0 at least, and a significant
enough change it'd warrant a lot of discussion and planning. We
do intend to start winding down support of BoltDB, though, and
new features may be added only to SQLite from here on.

I have added an escape hatch via an undocumented environment
variable that allows us to continue testing BoltDB in CI (and, if
necessary, locally) but I don't want this to be used for any
purpose except continued testing of the old DB to ensure we don't
break it.

Signed-off-by: Matt Heon <mheon@redhat.com>
This commit is contained in:
Matt Heon
2024-01-18 14:22:09 -05:00
parent 34e9146f63
commit cf0b436b96
6 changed files with 61 additions and 3 deletions

View File

@ -6,7 +6,9 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"io/fs"
"net" "net"
"os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -78,6 +80,19 @@ func NewBoltState(path string, runtime *Runtime) (State, error) {
logrus.Debugf("Initializing boltdb state at %s", path) logrus.Debugf("Initializing boltdb state at %s", path)
// BoltDB is deprecated and, as of Podman 5.0, we no longer allow the
// creation of new Bolt states.
// If the DB does not already exist, error out.
// To continue testing in CI, allow creation iff an undocumented env
// var is set.
if os.Getenv("CI_DESIRED_DATABASE") != "boltdb" {
if _, err := os.Stat(path); err != nil && errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("the BoltDB backend has been deprecated, no new BoltDB databases can be created: %w", define.ErrInvalidArg)
}
} else {
logrus.Debugf("Allowing deprecated database backend due to CI_DESIRED_DATABASE.")
}
db, err := bolt.Open(path, 0600, nil) db, err := bolt.Open(path, 0600, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("opening database %s: %w", path, err) return nil, fmt.Errorf("opening database %s: %w", path, err)

View File

@ -46,6 +46,10 @@ func getEmptyBoltState() (_ State, _ string, _ lock.Manager, retErr error) {
} }
}() }()
if err := os.Setenv("CI_DESIRED_DATABASE", "boltdb"); err != nil {
return nil, "", nil, err
}
dbPath := filepath.Join(tmpDir, "db.sql") dbPath := filepath.Join(tmpDir, "db.sql")
lockManager, err := lock.NewInMemoryManager(16) lockManager, err := lock.NewInMemoryManager(16)

View File

@ -186,8 +186,11 @@ var _ = Describe("Podman Info", func() {
Expect(session.OutputToString()).To(Equal(want)) Expect(session.OutputToString()).To(Equal(want))
}) })
It("podman --db-backend info basic check", func() { It("podman --db-backend info basic check", Serial, func() {
SkipIfRemote("--db-backend only supported on the local client") SkipIfRemote("--db-backend only supported on the local client")
const desiredDB = "CI_DESIRED_DATABASE"
type argWant struct { type argWant struct {
arg string arg string
want string want string
@ -199,14 +202,29 @@ var _ = Describe("Podman Info", func() {
// now because a boltdb exists it should use boltdb when default is requested // now because a boltdb exists it should use boltdb when default is requested
{arg: "", want: "boltdb"}, {arg: "", want: "boltdb"},
{arg: "sqlite", want: "sqlite"}, {arg: "sqlite", want: "sqlite"},
// just because we requested sqlite doesn't mean it stays that way.
// once a boltdb exists, podman will forevermore stick with it
{arg: "", want: "boltdb"},
} }
for _, tt := range backends { for _, tt := range backends {
oldDesiredDB := os.Getenv(desiredDB)
if tt.arg == "boltdb" {
err := os.Setenv(desiredDB, "boltdb")
Expect(err).To(Not(HaveOccurred()))
defer os.Setenv(desiredDB, oldDesiredDB)
}
session := podmanTest.Podman([]string{"--db-backend", tt.arg, "--log-level=info", "info", "--format", "{{.Host.DatabaseBackend}}"}) session := podmanTest.Podman([]string{"--db-backend", tt.arg, "--log-level=info", "info", "--format", "{{.Host.DatabaseBackend}}"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0)) Expect(session).To(Exit(0))
Expect(session.OutputToString()).To(Equal(tt.want)) Expect(session.OutputToString()).To(Equal(tt.want))
Expect(session.ErrorToString()).To(ContainSubstring("Using %s as database backend", tt.want)) Expect(session.ErrorToString()).To(ContainSubstring("Using %s as database backend", tt.want))
if tt.arg == "boltdb" {
err := os.Setenv(desiredDB, oldDesiredDB)
Expect(err).To(Not(HaveOccurred()))
}
} }
// make sure we get an error for bogus values // make sure we get an error for bogus values

View File

@ -52,7 +52,7 @@ var _ = Describe("Systemd activate", func() {
systemdArgs := []string{ systemdArgs := []string{
"-E", "http_proxy", "-E", "https_proxy", "-E", "no_proxy", "-E", "http_proxy", "-E", "https_proxy", "-E", "no_proxy",
"-E", "HTTP_PROXY", "-E", "HTTPS_PROXY", "-E", "NO_PROXY", "-E", "HTTP_PROXY", "-E", "HTTPS_PROXY", "-E", "NO_PROXY",
"-E", "XDG_RUNTIME_DIR", "-E", "XDG_RUNTIME_DIR", "-E", "CI_DESIRED_DATABASE",
"--listen", addr, "--listen", addr,
podmanTest.PodmanBinary} podmanTest.PodmanBinary}
systemdArgs = append(systemdArgs, podmanOptions...) systemdArgs = append(systemdArgs, podmanOptions...)
@ -114,7 +114,7 @@ var _ = Describe("Systemd activate", func() {
// start systemd activation with datagram socket // start systemd activation with datagram socket
activateSession := testUtils.StartSystemExec(activate, []string{ activateSession := testUtils.StartSystemExec(activate, []string{
"--datagram", "--listen", addr, "--datagram", "--listen", addr, "-E", "CI_DESIRED_DATABASE",
podmanTest.PodmanBinary, podmanTest.PodmanBinary,
"--root=" + filepath.Join(tempdir, "server_root"), "--root=" + filepath.Join(tempdir, "server_root"),
"system", "service", "system", "service",

View File

@ -255,4 +255,20 @@ EOF
} }
@test "podman - BoltDB cannot create new databases" {
skip_if_remote "DB checks only work for local Podman"
safe_opts=$(podman_isolation_opts ${PODMAN_TMPDIR})
CI_DESIRED_DATABASE= run_podman 125 $safe_opts --db-backend=boltdb info
assert "$output" =~ "deprecated, no new BoltDB databases can be created" \
"without CI_DESIRED_DATABASE"
CI_DESIRED_DATABASE=boltdb run_podman $safe_opts --log-level=debug --db-backend=boltdb info
assert "$output" =~ "Allowing deprecated database backend" \
"with CI_DESIRED_DATABASE"
run_podman $safe_opts system reset --force
}
# vim: filetype=sh # vim: filetype=sh

View File

@ -83,6 +83,11 @@ verify_iid_and_name() {
@test "podman image scp transfer" { @test "podman image scp transfer" {
skip_if_remote "only applicable under local podman" skip_if_remote "only applicable under local podman"
# See https://github.com/containers/podman/pull/21300 for details
if [[ "$CI_DESIRED_DATABASE" = "boltdb" ]]; then
skip "impossible due to pitfalls in our SSH implementation"
fi
# The testing is the same whether we're root or rootless; all that # The testing is the same whether we're root or rootless; all that
# differs is the destination (not-me) username. # differs is the destination (not-me) username.
if is_rootless; then if is_rootless; then