Merge pull request #1918 from mheon/use_db_paths

Use paths written in DB instead if they differ from our defaults
This commit is contained in:
OpenShift Merge Robot 2018-12-05 00:55:48 -08:00 committed by GitHub
commit 50e754cd57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 300 additions and 113 deletions

View File

@ -67,7 +67,7 @@ func createCmd(c *cli.Context) error {
rootless.SetSkipStorageSetup(true)
}
runtime, err := libpodruntime.GetContainerRuntime(c)
runtime, err := libpodruntime.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}

View File

@ -11,32 +11,18 @@ import (
// GetRuntime generates a new libpod runtime configured by command line options
func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
storageOpts, err := util.GetDefaultStoreOptions()
if err != nil {
return nil, err
}
return GetRuntimeWithStorageOpts(c, &storageOpts)
}
// GetContainerRuntime generates a new libpod runtime configured by command line options for containers
func GetContainerRuntime(c *cli.Context) (*libpod.Runtime, error) {
mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
if err != nil {
return nil, err
}
storageOpts, err := util.GetDefaultStoreOptions()
if err != nil {
return nil, err
}
storageOpts.UIDMap = mappings.UIDMap
storageOpts.GIDMap = mappings.GIDMap
return GetRuntimeWithStorageOpts(c, &storageOpts)
}
// GetRuntime generates a new libpod runtime configured by command line options
func GetRuntimeWithStorageOpts(c *cli.Context, storageOpts *storage.StoreOptions) (*libpod.Runtime, error) {
storageOpts := new(storage.StoreOptions)
options := []libpod.RuntimeOption{}
if c.IsSet("uidmap") || c.IsSet("gidmap") || c.IsSet("subuidmap") || c.IsSet("subgidmap") {
mappings, err := util.ParseIDMapping(c.StringSlice("uidmap"), c.StringSlice("gidmap"), c.String("subuidmap"), c.String("subgidmap"))
if err != nil {
return nil, err
}
storageOpts.UIDMap = mappings.UIDMap
storageOpts.GIDMap = mappings.GIDMap
}
if c.GlobalIsSet("root") {
storageOpts.GraphRoot = c.GlobalString("root")
}

View File

@ -44,7 +44,7 @@ func runCmd(c *cli.Context) error {
rootless.SetSkipStorageSetup(true)
}
runtime, err := libpodruntime.GetContainerRuntime(c)
runtime, err := libpodruntime.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}

View File

@ -3,7 +3,6 @@ package libpod
import (
"bytes"
"encoding/json"
"os"
"strings"
"sync"
@ -19,7 +18,6 @@ type BoltState struct {
dbLock sync.Mutex
namespace string
namespaceBytes []byte
lockDir string
runtime *Runtime
}
@ -52,25 +50,15 @@ type BoltState struct {
// containers/storage do not occur.
// NewBoltState creates a new bolt-backed state database
func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
func NewBoltState(path string, runtime *Runtime) (State, error) {
state := new(BoltState)
state.dbPath = path
state.lockDir = lockDir
state.runtime = runtime
state.namespace = ""
state.namespaceBytes = nil
logrus.Debugf("Initializing boltdb state at %s", path)
// Make the directory that will hold container lockfiles
if err := os.MkdirAll(lockDir, 0750); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating lockfiles dir %s", lockDir)
}
}
state.lockDir = lockDir
db, err := bolt.Open(path, 0600, nil)
if err != nil {
return nil, errors.Wrapf(err, "error opening database %s", path)
@ -115,11 +103,6 @@ func NewBoltState(path, lockDir string, runtime *Runtime) (State, error) {
return nil, errors.Wrapf(err, "error creating initial database layout")
}
// Check runtime configuration
if err := checkRuntimeConfig(db, runtime); err != nil {
return nil, err
}
state.valid = true
return state, nil
@ -240,6 +223,72 @@ func (s *BoltState) Refresh() error {
return err
}
// GetDBConfig retrieves runtime configuration fields that were created when
// the database was first initialized
func (s *BoltState) GetDBConfig() (*DBConfig, error) {
if !s.valid {
return nil, ErrDBClosed
}
cfg := new(DBConfig)
db, err := s.getDBCon()
if err != nil {
return nil, err
}
defer s.closeDBCon(db)
err = db.View(func(tx *bolt.Tx) error {
configBucket, err := getRuntimeConfigBucket(tx)
if err != nil {
return nil
}
// Some of these may be nil
// When we convert to string, Go will coerce them to ""
// That's probably fine - we could raise an error if the key is
// missing, but just not including it is also OK.
libpodRoot := configBucket.Get(staticDirKey)
libpodTmp := configBucket.Get(tmpDirKey)
storageRoot := configBucket.Get(graphRootKey)
storageTmp := configBucket.Get(runRootKey)
graphDriver := configBucket.Get(graphDriverKey)
cfg.LibpodRoot = string(libpodRoot)
cfg.LibpodTmp = string(libpodTmp)
cfg.StorageRoot = string(storageRoot)
cfg.StorageTmp = string(storageTmp)
cfg.GraphDriver = string(graphDriver)
return nil
})
if err != nil {
return nil, err
}
return cfg, nil
}
// ValidateDBConfig validates paths in the given runtime against the database
func (s *BoltState) ValidateDBConfig(runtime *Runtime) error {
if !s.valid {
return ErrDBClosed
}
db, err := s.getDBCon()
if err != nil {
return err
}
defer s.closeDBCon(db)
// Check runtime configuration
if err := checkRuntimeConfig(db, runtime); err != nil {
return err
}
return nil
}
// SetNamespace sets the namespace that will be used for container and pod
// retrieval
func (s *BoltState) SetNamespace(ns string) error {

View File

@ -30,6 +30,13 @@ const (
containersName = "containers"
podIDName = "pod-id"
namespaceName = "namespace"
staticDirName = "static-dir"
tmpDirName = "tmp-dir"
runRootName = "run-root"
graphRootName = "graph-root"
graphDriverName = "graph-driver-name"
osName = "os"
)
var (
@ -49,21 +56,19 @@ var (
containersBkt = []byte(containersName)
podIDKey = []byte(podIDName)
namespaceKey = []byte(namespaceName)
staticDirKey = []byte(staticDirName)
tmpDirKey = []byte(tmpDirName)
runRootKey = []byte(runRootName)
graphRootKey = []byte(graphRootName)
graphDriverKey = []byte(graphDriverName)
osKey = []byte(osName)
)
// Check if the configuration of the database is compatible with the
// configuration of the runtime opening it
// If there is no runtime configuration loaded, load our own
func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error {
var (
staticDir = []byte("static-dir")
tmpDir = []byte("tmp-dir")
runRoot = []byte("run-root")
graphRoot = []byte("graph-root")
graphDriverName = []byte("graph-driver-name")
osKey = []byte("os")
)
err := db.Update(func(tx *bolt.Tx) error {
configBkt, err := getRuntimeConfigBucket(tx)
if err != nil {
@ -74,31 +79,31 @@ func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error {
return err
}
if err := validateDBAgainstConfig(configBkt, "static dir",
rt.config.StaticDir, staticDir, ""); err != nil {
if err := validateDBAgainstConfig(configBkt, "libpod root directory (staticdir)",
rt.config.StaticDir, staticDirKey, ""); err != nil {
return err
}
if err := validateDBAgainstConfig(configBkt, "tmp dir",
rt.config.TmpDir, tmpDir, ""); err != nil {
if err := validateDBAgainstConfig(configBkt, "libpod temporary files directory (tmpdir)",
rt.config.TmpDir, tmpDirKey, ""); err != nil {
return err
}
if err := validateDBAgainstConfig(configBkt, "run root",
rt.config.StorageConfig.RunRoot, runRoot,
if err := validateDBAgainstConfig(configBkt, "storage temporary directory (runroot)",
rt.config.StorageConfig.RunRoot, runRootKey,
storage.DefaultStoreOptions.RunRoot); err != nil {
return err
}
if err := validateDBAgainstConfig(configBkt, "graph root",
rt.config.StorageConfig.GraphRoot, graphRoot,
if err := validateDBAgainstConfig(configBkt, "storage graph root directory (graphroot)",
rt.config.StorageConfig.GraphRoot, graphRootKey,
storage.DefaultStoreOptions.GraphRoot); err != nil {
return err
}
return validateDBAgainstConfig(configBkt, "graph driver name",
return validateDBAgainstConfig(configBkt, "storage graph driver",
rt.config.StorageConfig.GraphDriverName,
graphDriverName,
graphDriverKey,
storage.DefaultStoreOptions.GraphDriverName)
})
@ -261,7 +266,7 @@ func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.
}
// Get the lock
lockPath := filepath.Join(s.lockDir, string(id))
lockPath := filepath.Join(s.runtime.lockDir, string(id))
lock, err := storage.GetLockfile(lockPath)
if err != nil {
return errors.Wrapf(err, "error retrieving lockfile for container %s", string(id))
@ -297,7 +302,7 @@ func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error
}
// Get the lock
lockPath := filepath.Join(s.lockDir, string(id))
lockPath := filepath.Join(s.runtime.lockDir, string(id))
lock, err := storage.GetLockfile(lockPath)
if err != nil {
return errors.Wrapf(err, "error retrieving lockfile for pod %s", string(id))

View File

@ -73,6 +73,18 @@ func (s *InMemoryState) Refresh() error {
return nil
}
// GetDBConfig is not implemented for in-memory state.
// As we do not store a config, return an empty one.
func (s *InMemoryState) GetDBConfig() (*DBConfig, error) {
return &DBConfig{}, nil
}
// ValidateDBConfig is not implemented for the in-memory state.
// Since we do nothing just return no error.
func (s *InMemoryState) ValidateDBConfig(runtime *Runtime) error {
return nil
}
// SetNamespace sets the namespace for container and pod retrieval.
func (s *InMemoryState) SetNamespace(ns string) error {
s.namespace = ns

View File

@ -29,19 +29,40 @@ func WithStorageConfig(config storage.StoreOptions) RuntimeOption {
return ErrRuntimeFinalized
}
rt.config.StorageConfig.RunRoot = config.RunRoot
rt.config.StorageConfig.GraphRoot = config.GraphRoot
rt.config.StorageConfig.GraphDriverName = config.GraphDriverName
rt.config.StaticDir = filepath.Join(config.GraphRoot, "libpod")
if config.RunRoot != "" {
rt.config.StorageConfig.RunRoot = config.RunRoot
rt.configuredFrom.storageRunRootSet = true
}
rt.config.StorageConfig.GraphDriverOptions = make([]string, len(config.GraphDriverOptions))
copy(rt.config.StorageConfig.GraphDriverOptions, config.GraphDriverOptions)
if config.GraphRoot != "" {
rt.config.StorageConfig.GraphRoot = config.GraphRoot
rt.configuredFrom.storageGraphRootSet = true
rt.config.StorageConfig.UIDMap = make([]idtools.IDMap, len(config.UIDMap))
copy(rt.config.StorageConfig.UIDMap, config.UIDMap)
// Also set libpod static dir, so we are a subdirectory
// of the c/storage store by default
rt.config.StaticDir = filepath.Join(config.GraphRoot, "libpod")
rt.configuredFrom.libpodStaticDirSet = true
}
rt.config.StorageConfig.GIDMap = make([]idtools.IDMap, len(config.GIDMap))
copy(rt.config.StorageConfig.GIDMap, config.GIDMap)
if config.GraphDriverName != "" {
rt.config.StorageConfig.GraphDriverName = config.GraphDriverName
rt.configuredFrom.storageGraphDriverSet = true
}
if config.GraphDriverOptions != nil {
rt.config.StorageConfig.GraphDriverOptions = make([]string, len(config.GraphDriverOptions))
copy(rt.config.StorageConfig.GraphDriverOptions, config.GraphDriverOptions)
}
if config.UIDMap != nil {
rt.config.StorageConfig.UIDMap = make([]idtools.IDMap, len(config.UIDMap))
copy(rt.config.StorageConfig.UIDMap, config.UIDMap)
}
if config.GIDMap != nil {
rt.config.StorageConfig.GIDMap = make([]idtools.IDMap, len(config.GIDMap))
copy(rt.config.StorageConfig.GIDMap, config.GIDMap)
}
return nil
}
@ -174,6 +195,7 @@ func WithStaticDir(dir string) RuntimeOption {
}
rt.config.StaticDir = dir
rt.configuredFrom.libpodStaticDirSet = true
return nil
}
@ -225,6 +247,7 @@ func WithTmpDir(dir string) RuntimeOption {
}
rt.config.TmpDir = dir
rt.configuredFrom.libpodTmpDirSet = true
return nil
}

View File

@ -83,6 +83,7 @@ type Runtime struct {
lock sync.RWMutex
imageRuntime *image.Runtime
firewallBackend firewall.FirewallBackend
configuredFrom *runtimeConfiguredFrom
}
// RuntimeConfig contains configuration options used to set up the runtime
@ -174,6 +175,20 @@ type RuntimeConfig struct {
EnableLabeling bool `toml:"label"`
}
// runtimeConfiguredFrom is a struct used during early runtime init to help
// assemble the full RuntimeConfig struct from defaults.
// It indicated whether several fields in the runtime configuration were set
// explicitly.
// If they were not, we may override them with information from the database,
// if it exists and differs from what is present in the system already.
type runtimeConfiguredFrom struct {
storageGraphDriverSet bool
storageGraphRootSet bool
storageRunRootSet bool
libpodStaticDirSet bool
libpodTmpDirSet bool
}
var (
defaultRuntimeConfig = RuntimeConfig{
// Leave this empty so containers/storage will use its defaults
@ -251,6 +266,7 @@ func SetXdgRuntimeDir(val string) error {
func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
runtime = new(Runtime)
runtime.config = new(RuntimeConfig)
runtime.configuredFrom = new(runtimeConfiguredFrom)
// Copy the default configuration
tmpDir, err := getDefaultTmpDir()
@ -260,6 +276,16 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
deepcopier.Copy(defaultRuntimeConfig).To(runtime.config)
runtime.config.TmpDir = tmpDir
if rootless.IsRootless() {
// If we're rootless, override the default storage config
storageConf, err := util.GetDefaultStoreOptions()
if err != nil {
return nil, errors.Wrapf(err, "error retrieving rootless storage config")
}
runtime.config.StorageConfig = storageConf
runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod")
}
configPath := ConfigPath
foundConfig := true
rootlessConfigPath := ""
@ -305,6 +331,25 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
if err != nil {
return nil, errors.Wrapf(err, "error reading configuration file %s", configPath)
}
// This is ugly, but we need to decode twice.
// Once to check if libpod static and tmp dirs were explicitly
// set (not enough to check if they're not the default value,
// might have been explicitly configured to the default).
// A second time to actually get a usable config.
tmpConfig := new(RuntimeConfig)
if _, err := toml.Decode(string(contents), tmpConfig); err != nil {
return nil, errors.Wrapf(err, "error decoding configuration file %s",
configPath)
}
if tmpConfig.StaticDir != "" {
runtime.configuredFrom.libpodStaticDirSet = true
}
if tmpConfig.TmpDir != "" {
runtime.configuredFrom.libpodTmpDirSet = true
}
if _, err := toml.Decode(string(contents), runtime.config); err != nil {
return nil, errors.Wrapf(err, "error decoding configuration file %s", configPath)
}
@ -346,6 +391,7 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime *Runtime, err error) {
runtime = new(Runtime)
runtime.config = new(RuntimeConfig)
runtime.configuredFrom = new(runtimeConfiguredFrom)
// Set two fields not in the TOML config
runtime.config.StateType = defaultRuntimeConfig.StateType
@ -424,6 +470,77 @@ func makeRuntime(runtime *Runtime) (err error) {
runtime.config.ConmonPath)
}
// Make the static files directory if it does not exist
if err := os.MkdirAll(runtime.config.StaticDir, 0700); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return errors.Wrapf(err, "error creating runtime static files directory %s",
runtime.config.StaticDir)
}
}
// Set up the state
switch runtime.config.StateType {
case InMemoryStateStore:
state, err := NewInMemoryState()
if err != nil {
return err
}
runtime.state = state
case SQLiteStateStore:
return errors.Wrapf(ErrInvalidArg, "SQLite state is currently disabled")
case BoltDBStateStore:
dbPath := filepath.Join(runtime.config.StaticDir, "bolt_state.db")
state, err := NewBoltState(dbPath, runtime)
if err != nil {
return err
}
runtime.state = state
default:
return errors.Wrapf(ErrInvalidArg, "unrecognized state type passed")
}
// Grab config from the database so we can reset some defaults
dbConfig, err := runtime.state.GetDBConfig()
if err != nil {
return errors.Wrapf(err, "error retrieving runtime configuration from database")
}
// Reset defaults if they were not explicitly set
if !runtime.configuredFrom.storageGraphDriverSet && dbConfig.GraphDriver != "" {
runtime.config.StorageConfig.GraphDriverName = dbConfig.GraphDriver
}
if !runtime.configuredFrom.storageGraphRootSet && dbConfig.StorageRoot != "" {
runtime.config.StorageConfig.GraphRoot = dbConfig.StorageRoot
}
if !runtime.configuredFrom.storageRunRootSet && dbConfig.StorageTmp != "" {
runtime.config.StorageConfig.RunRoot = dbConfig.StorageTmp
}
if !runtime.configuredFrom.libpodStaticDirSet && dbConfig.LibpodRoot != "" {
runtime.config.StaticDir = dbConfig.LibpodRoot
}
if !runtime.configuredFrom.libpodTmpDirSet && dbConfig.LibpodTmp != "" {
runtime.config.TmpDir = dbConfig.LibpodTmp
}
logrus.Debugf("Using graph driver %s", runtime.config.StorageConfig.GraphDriverName)
logrus.Debugf("Using graph root %s", runtime.config.StorageConfig.GraphRoot)
logrus.Debugf("Using run root %s", runtime.config.StorageConfig.RunRoot)
logrus.Debugf("Using static dir %s", runtime.config.StaticDir)
logrus.Debugf("Using tmp dir %s", runtime.config.TmpDir)
// Validate our config against the database, now that we've set our
// final storage configuration
if err := runtime.state.ValidateDBConfig(runtime); err != nil {
return err
}
if err := runtime.state.SetNamespace(runtime.config.Namespace); err != nil {
return errors.Wrapf(err, "error setting libpod namespace in state")
}
logrus.Debugf("Set libpod namespace to %q", runtime.config.Namespace)
// Set up containers/storage
var store storage.Store
if rootless.SkipStorageSetup() {
@ -491,15 +608,6 @@ func makeRuntime(runtime *Runtime) (err error) {
}
runtime.ociRuntime = ociRuntime
// Make the static files directory if it does not exist
if err := os.MkdirAll(runtime.config.StaticDir, 0755); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return errors.Wrapf(err, "error creating runtime static files directory %s",
runtime.config.StaticDir)
}
}
// Make a directory to hold container lockfiles
lockDir := filepath.Join(runtime.config.TmpDir, "lock")
if err := os.MkdirAll(lockDir, 0755); err != nil {
@ -540,33 +648,6 @@ func makeRuntime(runtime *Runtime) (err error) {
}
runtime.firewallBackend = fwBackend
// Set up the state
switch runtime.config.StateType {
case InMemoryStateStore:
state, err := NewInMemoryState()
if err != nil {
return err
}
runtime.state = state
case SQLiteStateStore:
return errors.Wrapf(ErrInvalidArg, "SQLite state is currently disabled")
case BoltDBStateStore:
dbPath := filepath.Join(runtime.config.StaticDir, "bolt_state.db")
state, err := NewBoltState(dbPath, runtime.lockDir, runtime)
if err != nil {
return err
}
runtime.state = state
default:
return errors.Wrapf(ErrInvalidArg, "unrecognized state type passed")
}
if err := runtime.state.SetNamespace(runtime.config.Namespace); err != nil {
return errors.Wrapf(err, "error setting libpod namespace in state")
}
logrus.Debugf("Set libpod namespace to %q", runtime.config.Namespace)
// We now need to see if the system has restarted
// We check for the presence of a file in our tmp directory to verify this
// This check must be locked to prevent races

View File

@ -1,5 +1,15 @@
package libpod
// DBConfig is a set of Libpod runtime configuration settings that are saved
// in a State when it is first created, and can subsequently be retrieved.
type DBConfig struct {
LibpodRoot string
LibpodTmp string
StorageRoot string
StorageTmp string
GraphDriver string
}
// State is a storage backend for libpod's current state.
// A State is only initialized once per instance of libpod.
// As such, initialization methods for State implementations may safely assume
@ -21,6 +31,22 @@ type State interface {
// Refresh clears container and pod states after a reboot
Refresh() error
// GetDBConfig retrieves several paths configured within the database
// when it was created - namely, Libpod root and tmp dirs, c/storage
// root and tmp dirs, and c/storage graph driver.
// This is not implemented by the in-memory state, as it has no need to
// validate runtime configuration.
GetDBConfig() (*DBConfig, error)
// ValidateDBConfig validates the config in the given Runtime struct
// against paths stored in the configured database.
// Libpod root and tmp dirs and c/storage root and tmp dirs and graph
// driver are validated.
// This is not implemented by the in-memory state, as it has no need to
// validate runtime configuration that may change over multiple runs of
// the program.
ValidateDBConfig(runtime *Runtime) error
// SetNamespace() sets the namespace for the store, and will determine
// what containers are retrieved with container and pod retrieval calls.
// A namespace of "", the empty string, acts as no namespace, and

View File

@ -45,11 +45,16 @@ func getEmptyBoltState() (s State, p string, p2 string, err error) {
dbPath := filepath.Join(tmpDir, "db.sql")
lockDir := filepath.Join(tmpDir, "locks")
if err := os.Mkdir(lockDir, 0755); err != nil {
return nil, "", "", err
}
runtime := new(Runtime)
runtime.config = new(RuntimeConfig)
runtime.config.StorageConfig = storage.StoreOptions{}
runtime.lockDir = lockDir
state, err := NewBoltState(dbPath, lockDir, runtime)
state, err := NewBoltState(dbPath, runtime)
if err != nil {
return nil, "", "", err
}

View File

@ -313,7 +313,7 @@ func getTomlStorage(storeOptions *storage.StoreOptions) *tomlConfig {
return config
}
// GetDefaultStoreOptions returns the storage ops for containers
// GetDefaultStoreOptions returns the default storage options for containers.
func GetDefaultStoreOptions() (storage.StoreOptions, error) {
storageOpts := storage.DefaultStoreOptions
if rootless.IsRootless() {