diff --git a/drivers/driver.go b/drivers/driver.go index 1c1f017432..9cbfa9e9ad 100644 --- a/drivers/driver.go +++ b/drivers/driver.go @@ -329,7 +329,7 @@ type Options struct { } // New creates the driver and initializes it at the specified root. -func New(name string, config Options) (Driver, error) { +func New(name string, driverPriority []string, config Options) (Driver, error) { if name != "" { logrus.Debugf("[graphdriver] trying provided driver %q", name) // so the logs show specified driver return GetDriver(name, config) @@ -337,7 +337,14 @@ func New(name string, config Options) (Driver, error) { // Guess for prior driver driversMap := scanPriorDrivers(config.Root) - for _, name := range priority { + + // use the supplied priority list unless it is empty + prioList := driverPriority + if len(prioList) == 0 { + prioList = priority + } + + for _, name := range prioList { if name == "vfs" { // don't use vfs even if there is state present. continue @@ -372,7 +379,7 @@ func New(name string, config Options) (Driver, error) { } // Check for priority drivers first - for _, name := range priority { + for _, name := range prioList { driver, err := getBuiltinDriver(name, config.Root, config) if err != nil { if isDriverNotSupported(err) { diff --git a/store.go b/store.go index f819722ba9..d4a9871119 100644 --- a/store.go +++ b/store.go @@ -591,8 +591,9 @@ type store struct { // The following fields are only set when constructing store, and must never be modified afterwards. // They are safe to access without any other locking. - runRoot string - graphDriverName string // Initially set to the user-requested value, possibly ""; updated during store construction, and does not change afterwards. + runRoot string + graphDriverName string // Initially set to the user-requested value, possibly ""; updated during store construction, and does not change afterwards. + graphDriverPriority []string // graphLock: // - Ensures that we always reload graphDriver, and the primary layer store, after any process does store.Shutdown. This is necessary // because (??) the Shutdown may forcibly unmount and clean up, affecting graph driver state in a way only a graph driver @@ -723,20 +724,21 @@ func GetStore(options types.StoreOptions) (Store, error) { autoNsMaxSize = AutoUserNsMaxSize } s := &store{ - runRoot: options.RunRoot, - graphDriverName: options.GraphDriverName, - graphLock: graphLock, - usernsLock: usernsLock, - graphRoot: options.GraphRoot, - graphOptions: options.GraphDriverOptions, - pullOptions: options.PullOptions, - uidMap: copyIDMap(options.UIDMap), - gidMap: copyIDMap(options.GIDMap), - autoUsernsUser: options.RootAutoNsUser, - autoNsMinSize: autoNsMinSize, - autoNsMaxSize: autoNsMaxSize, - disableVolatile: options.DisableVolatile, - transientStore: options.TransientStore, + runRoot: options.RunRoot, + graphDriverName: options.GraphDriverName, + graphDriverPriority: options.GraphDriverPriority, + graphLock: graphLock, + usernsLock: usernsLock, + graphRoot: options.GraphRoot, + graphOptions: options.GraphDriverOptions, + pullOptions: options.PullOptions, + uidMap: copyIDMap(options.UIDMap), + gidMap: copyIDMap(options.GIDMap), + autoUsernsUser: options.RootAutoNsUser, + autoNsMinSize: autoNsMinSize, + autoNsMaxSize: autoNsMaxSize, + disableVolatile: options.DisableVolatile, + transientStore: options.TransientStore, additionalUIDs: nil, additionalGIDs: nil, @@ -940,7 +942,7 @@ func (s *store) createGraphDriverLocked() (drivers.Driver, error) { UIDMaps: s.uidMap, GIDMaps: s.gidMap, } - return drivers.New(s.graphDriverName, config) + return drivers.New(s.graphDriverName, s.graphDriverPriority, config) } func (s *store) GraphDriver() (drivers.Driver, error) { diff --git a/types/options.go b/types/options.go index 361cbf24f9..09f9687292 100644 --- a/types/options.go +++ b/types/options.go @@ -19,6 +19,7 @@ import ( type TomlConfig struct { Storage struct { Driver string `toml:"driver,omitempty"` + DriverPriority []string `toml:"driver_priority,omitempty"` RunRoot string `toml:"runroot,omitempty"` GraphRoot string `toml:"graphroot,omitempty"` RootlessStoragePath string `toml:"rootless_storage_path,omitempty"` @@ -213,10 +214,16 @@ type StoreOptions struct { // RootlessStoragePath is the storage path for rootless users // default $HOME/.local/share/containers/storage RootlessStoragePath string `toml:"rootless_storage_path"` - // GraphDriverName is the underlying storage driver that we'll be - // using. It only needs to be specified the first time a Store is - // initialized for a given RunRoot and GraphRoot. + // If the driver is not specified, the best suited driver will be picked + // either from GraphDriverPriority or from the platform dependent + // priority list (in that order). GraphDriverName string `json:"driver,omitempty"` + // GraphDriverPriority is a list of storage drivers that will be tried + // to initialize the Store for a given RunRoot and GraphRoot unless a + // GraphDriverName is set. + // This list can be used to define a custom order in which the drivers + // will be tried. + GraphDriverPriority []string `json:"driver-priority,omitempty"` // GraphDriverOptions are driver-specific options. GraphDriverOptions []string `json:"driver-options,omitempty"` // UIDMap and GIDMap are used for setting up a container's root filesystem @@ -383,6 +390,7 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) erro if storeOptions.GraphDriverName == "" { logrus.Errorf("The storage 'driver' option must be set in %s to guarantee proper operation", configFile) } + storeOptions.GraphDriverPriority = config.Storage.DriverPriority if config.Storage.RunRoot != "" { storeOptions.RunRoot = config.Storage.RunRoot }