diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 85 | ||||
-rw-r--r-- | libpod/boltdb_state_internal.go | 47 | ||||
-rw-r--r-- | libpod/in_memory_state.go | 12 | ||||
-rw-r--r-- | libpod/options.go | 43 | ||||
-rw-r--r-- | libpod/runtime.go | 153 | ||||
-rw-r--r-- | libpod/state.go | 26 | ||||
-rw-r--r-- | libpod/state_test.go | 7 |
7 files changed, 287 insertions, 86 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 42f029379..cb661d4e9 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -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 { diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index cc7d106cc..3f00657ea 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -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)) diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 78e765ccd..77eba0cc6 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -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 diff --git a/libpod/options.go b/libpod/options.go index e1d0b5007..3e43d73f0 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -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 } diff --git a/libpod/runtime.go b/libpod/runtime.go index 55a300bb0..6b6a8cc2d 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -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 diff --git a/libpod/state.go b/libpod/state.go index 273e81318..06c2003d8 100644 --- a/libpod/state.go +++ b/libpod/state.go @@ -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 diff --git a/libpod/state_test.go b/libpod/state_test.go index 04572fb29..d93a371f3 100644 --- a/libpod/state_test.go +++ b/libpod/state_test.go @@ -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 } |