From a19975f96d2ee7efe186d9aa0be42285cfafa3f4 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 12 Jun 2019 05:13:26 -0400 Subject: If container is not in correct state podman exec should exit with 126 This way a tool can determine if the container exists or not, but is in the wrong state. Since 126 is documeted as: **_126_** if the **_contained command_** cannot be invoked It makes sense that the container would exit with this state. Signed-off-by: Daniel J Walsh Signed-off-by: Matthew Heon --- cmd/podman/exec.go | 9 ++++++++- libpod/container_api.go | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index 073e72e64..9e265b48c 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -109,5 +109,12 @@ func execCmd(c *cli.Context) error { envs = append(envs, fmt.Sprintf("%s=%s", k, v)) } - return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir")) + if err := ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir")); err != nil { + if errors.Cause(err) == libpod.ErrCtrStateInvalid { + exitCode = 126 + } + return err + } + + return nil } diff --git a/libpod/container_api.go b/libpod/container_api.go index 4eaf737b0..832801aeb 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -285,7 +285,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir // TODO can probably relax this once we track exec sessions if conState != ContainerStateRunning { - return errors.Errorf("cannot exec into container that is not running") + return errors.Wrapf(ErrCtrStateInvalid, "cannot exec into container that is not running") } if privileged || c.config.Privileged { capList = caps.GetAllCapabilities() -- cgit v1.2.3-54-g00ecf From 6c8798b37f0c444a8006718eb0da96abffcbc3d8 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 12 Jun 2019 05:06:00 -0400 Subject: Document exit codes for podman exec Also fix podman run exit codes to show real messages when failures happen. Signed-off-by: Daniel J Walsh Signed-off-by: Matthew Heon --- docs/podman-exec.1.md | 34 ++++++++++++++++++++++++++++++++++ docs/podman-run.1.md | 17 +++++++---------- docs/podman.1.md | 11 ++++------- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/docs/podman-exec.1.md b/docs/podman-exec.1.md index 77317b0ca..906109f87 100644 --- a/docs/podman-exec.1.md +++ b/docs/podman-exec.1.md @@ -46,6 +46,40 @@ The default working directory for running binaries within a container is the roo The image developer can set a different default with the WORKDIR instruction, which can be overridden when creating the container. +## Exit Status + +The exit code from `podman exec` gives information about why the command within the container failed to run or why it exited. When `podman exec` exits with a +non-zero code, the exit codes follow the `chroot` standard, see below: + +**_125_** if the error is with podman **_itself_** + + $ podman exec --foo ctrID /bin/sh; echo $? + Error: unknown flag: --foo + 125 + +**_126_** if the **_contained command_** cannot be invoked + + $ podman exec ctrID /etc; echo $? + Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error + 126 + +**_127_** if the **_contained command_** cannot be found + + $ podman exec ctrID foo; echo $? + Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error + 127 + +**_Exit code_** of **_contained command_** otherwise + + $ podman exec ctrID /bin/sh -c 'exit 3' + # 3 + +## EXAMPLES + +$ podman exec -it ctrID ls +$ podman exec -it -w /tmp myCtr pwd +$ podman exec --user root ctrID ls + ## SEE ALSO podman(1), podman-run(1) diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index 8b96ea6d9..ee51c3bb8 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -819,28 +819,25 @@ the exit codes follow the `chroot` standard, see below: **_125_** if the error is with podman **_itself_** $ podman run --foo busybox; echo $? - # flag provided but not defined: --foo - See 'podman run --help'. - 125 + Error: unknown flag: --foo + 125 **_126_** if the **_contained command_** cannot be invoked $ podman run busybox /etc; echo $? - # exec: "/etc": permission denied - podman: Error response from daemon: Contained command could not be invoked - 126 + Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error + 126 **_127_** if the **_contained command_** cannot be found $ podman run busybox foo; echo $? - # exec: "foo": executable file not found in $PATH - podman: Error response from daemon: Contained command not found or does not exist - 127 + Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error + 127 **_Exit code_** of **_contained command_** otherwise $ podman run busybox /bin/sh -c 'exit 3' - # 3 + 3 ## EXAMPLES diff --git a/docs/podman.1.md b/docs/podman.1.md index a73ebb55e..9be41c3f6 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -98,24 +98,21 @@ the exit codes follow the `chroot` standard, see below: **_125_** if the error is with podman **_itself_** $ podman run --foo busybox; echo $? - # flag provided but not defined: --foo - See 'podman run --help'. + Error: unknown flag: --foo 125 **_126_** if executing a **_container command_** and the the **_command_** cannot be invoked $ podman run busybox /etc; echo $? - # exec: "/etc": permission denied - podman: Error response from daemon: Contained command could not be invoked + Error: container_linux.go:346: starting container process caused "exec: \"/etc\": permission denied": OCI runtime error 126 **_127_** if executing a **_container command_** and the the **_command_** cannot be found $ podman run busybox foo; echo $? - # exec: "foo": executable file not found in $PATH - podman: Error response from daemon: Contained command not found or does not exist + Error: container_linux.go:346: starting container process caused "exec: \"foo\": executable file not found in $PATH": OCI runtime error 127 -**_Exit code_** of **_container command_** otherwise +**_Exit code_** of **_contained command_** otherwise $ podman run busybox /bin/sh -c 'exit 3' # 3 -- cgit v1.2.3-54-g00ecf From 5995f1c9b954cb998572e9e26176b312bbdbfd02 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 20 Jun 2019 14:04:16 -0400 Subject: Make configuration validation not require a DB commit If there are missing fields, we still require a commit, but that should not happen often. Signed-off-by: Matthew Heon --- libpod/boltdb_state_internal.go | 170 +++++++++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 55 deletions(-) diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 06f8dcb24..1c746c724 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -71,89 +71,149 @@ var ( osKey = []byte(osName) ) +// This represents a field in the runtime configuration that will be validated +// against the DB to ensure no configuration mismatches occur. +type dbConfigValidation struct { + name string // Only used for error messages + runtimeValue string + key []byte + defaultValue string +} + // 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 { - err := db.Update(func(tx *bolt.Tx) error { + // We need to validate the following things + checks := []dbConfigValidation{ + { + "OS", + runtime.GOOS, + osKey, + runtime.GOOS, + }, + { + "libpod root directory (staticdir)", + rt.config.StaticDir, + staticDirKey, + "", + }, + { + "libpod temporary files directory (tmpdir)", + rt.config.TmpDir, + tmpDirKey, + "", + }, + { + "storage temporary directory (runroot)", + rt.config.StorageConfig.RunRoot, + runRootKey, + storage.DefaultStoreOptions.RunRoot, + }, + { + "storage graph root directory (graphroot)", + rt.config.StorageConfig.GraphRoot, + graphRootKey, + storage.DefaultStoreOptions.GraphRoot, + }, + { + "storage graph driver", + rt.config.StorageConfig.GraphDriverName, + graphDriverKey, + storage.DefaultStoreOptions.GraphDriverName, + }, + } + + // These fields were missing and will have to be recreated. + missingFields := []dbConfigValidation{} + + // Let's try and validate read-only first + err := db.View(func(tx *bolt.Tx) error { configBkt, err := getRuntimeConfigBucket(tx) if err != nil { return err } - if err := validateDBAgainstConfig(configBkt, "OS", runtime.GOOS, osKey, runtime.GOOS); err != nil { - return err + for _, check := range checks { + exists, err := readOnlyValidateConfig(configBkt, check) + if err != nil { + return err + } + if !exists { + missingFields = append(missingFields, check) + } } - if err := validateDBAgainstConfig(configBkt, "libpod root directory (staticdir)", - rt.config.StaticDir, staticDirKey, ""); err != nil { - return err - } + return nil + }) + if err != nil { + return err + } - if err := validateDBAgainstConfig(configBkt, "libpod temporary files directory (tmpdir)", - rt.config.TmpDir, tmpDirKey, ""); err != nil { - return err - } + if len(missingFields) == 0 { + return nil + } - if err := validateDBAgainstConfig(configBkt, "storage temporary directory (runroot)", - rt.config.StorageConfig.RunRoot, runRootKey, - storage.DefaultStoreOptions.RunRoot); err != nil { + // Populate missing fields + return db.Update(func(tx *bolt.Tx) error { + configBkt, err := getRuntimeConfigBucket(tx) + if err != nil { return err } - if err := validateDBAgainstConfig(configBkt, "storage graph root directory (graphroot)", - rt.config.StorageConfig.GraphRoot, graphRootKey, - storage.DefaultStoreOptions.GraphRoot); err != nil { - return err + for _, missing := range missingFields { + dbValue := []byte(missing.runtimeValue) + if missing.runtimeValue == "" && missing.defaultValue != "" { + dbValue = []byte(missing.defaultValue) + } + + if err := configBkt.Put(missing.key, dbValue); err != nil { + return errors.Wrapf(err, "error updating %s in DB runtime config", missing.name) + } } - return validateDBAgainstConfig(configBkt, "storage graph driver", - rt.config.StorageConfig.GraphDriverName, - graphDriverKey, - storage.DefaultStoreOptions.GraphDriverName) + return nil }) - - return err } -// Validate a configuration entry in the DB against current runtime config -// If the given configuration key does not exist it will be created -// If the given runtimeValue or value retrieved from the database are the empty -// string and defaultValue is not, defaultValue will be checked instead. This -// ensures that we will not fail on configuration changes in configured c/storage. -func validateDBAgainstConfig(bucket *bolt.Bucket, fieldName, runtimeValue string, keyName []byte, defaultValue string) error { - keyBytes := bucket.Get(keyName) +// Attempt a read-only validation of a configuration entry in the DB against an +// element of the current runtime configuration. +// If the configuration key in question does not exist, (false, nil) will be +// returned. +// If the configuration key does exist, and matches the runtime configuration +// successfully, (true, nil) is returned. +// An error is only returned when validation fails. +// if the given runtimeValue or value retrieved from the database are empty, +// and defaultValue is not, defaultValue will be checked instead. This ensures +// that we will not fail on configuration changes in c/storage (where we may +// pass the empty string to use defaults). +func readOnlyValidateConfig(bucket *bolt.Bucket, toCheck dbConfigValidation) (bool, error) { + keyBytes := bucket.Get(toCheck.key) if keyBytes == nil { - dbValue := []byte(runtimeValue) - if runtimeValue == "" && defaultValue != "" { - dbValue = []byte(defaultValue) - } + // False return indicates missing key + return false, nil + } - if err := bucket.Put(keyName, dbValue); err != nil { - return errors.Wrapf(err, "error updating %s in DB runtime config", fieldName) - } - } else { - if runtimeValue != string(keyBytes) { - // If runtimeValue is the empty string, check against - // the default - if runtimeValue == "" && defaultValue != "" && - string(keyBytes) == defaultValue { - return nil - } + dbValue := string(keyBytes) - // If DB value is the empty string, check that the - // runtime value is the default - if string(keyBytes) == "" && defaultValue != "" && - runtimeValue == defaultValue { - return nil - } + if toCheck.runtimeValue != dbValue { + // If the runtime value is the empty string and default is not, + // check against default. + if toCheck.runtimeValue == "" && toCheck.defaultValue != "" && dbValue == toCheck.defaultValue { + return true, nil + } - return errors.Wrapf(ErrDBBadConfig, "database %s %s does not match our %s %s", - fieldName, string(keyBytes), fieldName, runtimeValue) + // If the DB value is the empty string, check that the runtime + // value is the default. + if dbValue == "" && toCheck.defaultValue != "" && toCheck.runtimeValue == toCheck.defaultValue { + return true, nil } + + return true, errors.Wrapf(ErrDBBadConfig, "database %s %q does not match our %s %q", + toCheck.name, dbValue, toCheck.name, toCheck.runtimeValue) } - return nil + return true, nil } // Open a connection to the database. -- cgit v1.2.3-54-g00ecf From 65fb42ad55647ebceb8057c4562da0d39da1382d Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 20 Jun 2019 10:17:38 -0400 Subject: Avoid a read-write transaction on DB init Instead, use a less expensive read-only transaction to see if the DB is ready for use (it probably is), and only fire the expensive RW transaction if absolutely necessary. Signed-off-by: Matthew Heon --- libpod/boltdb_state.go | 70 ++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index b154d8bda..fdae08bf3 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -71,42 +71,50 @@ func NewBoltState(path string, runtime *Runtime) (State, error) { // As such, just a db.Close() is fine here. defer db.Close() - // Perform initial database setup - err = db.Update(func(tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists(idRegistryBkt); err != nil { - return errors.Wrapf(err, "error creating id-registry bucket") - } - if _, err := tx.CreateBucketIfNotExists(nameRegistryBkt); err != nil { - return errors.Wrapf(err, "error creating name-registry bucket") - } - if _, err := tx.CreateBucketIfNotExists(nsRegistryBkt); err != nil { - return errors.Wrapf(err, "error creating ns-registry bucket") - } - if _, err := tx.CreateBucketIfNotExists(ctrBkt); err != nil { - return errors.Wrapf(err, "error creating containers bucket") - } - if _, err := tx.CreateBucketIfNotExists(allCtrsBkt); err != nil { - return errors.Wrapf(err, "error creating all containers bucket") - } - if _, err := tx.CreateBucketIfNotExists(podBkt); err != nil { - return errors.Wrapf(err, "error creating pods bucket") - } - if _, err := tx.CreateBucketIfNotExists(allPodsBkt); err != nil { - return errors.Wrapf(err, "error creating all pods bucket") - } - if _, err := tx.CreateBucketIfNotExists(volBkt); err != nil { - return errors.Wrapf(err, "error creating volume bucket") - } - if _, err := tx.CreateBucketIfNotExists(allVolsBkt); err != nil { - return errors.Wrapf(err, "error creating all volumes bucket") + createBuckets := [][]byte{ + idRegistryBkt, + nameRegistryBkt, + nsRegistryBkt, + ctrBkt, + allCtrsBkt, + podBkt, + allPodsBkt, + volBkt, + allVolsBkt, + runtimeConfigBkt, + } + + // Does the DB need an update? + needsUpdate := false + err = db.View(func(tx *bolt.Tx) error { + for _, bkt := range createBuckets { + if test := tx.Bucket(bkt); test == nil { + needsUpdate = true + break + } } - if _, err := tx.CreateBucketIfNotExists(runtimeConfigBkt); err != nil { - return errors.Wrapf(err, "error creating runtime-config bucket") + return nil + }) + if err != nil { + return nil, errors.Wrapf(err, "error checking DB schema") + } + + if !needsUpdate { + state.valid = true + return state, nil + } + + // Ensure schema is properly created in DB + err = db.Update(func(tx *bolt.Tx) error { + for _, bkt := range createBuckets { + if _, err := tx.CreateBucketIfNotExists(bkt); err != nil { + return errors.Wrapf(err, "error creating bucket %s", string(bkt)) + } } return nil }) if err != nil { - return nil, errors.Wrapf(err, "error creating initial database layout") + return nil, errors.Wrapf(err, "error creating buckets for DB") } state.valid = true -- cgit v1.2.3-54-g00ecf From 90b835db69d589de559462d988cb3fae5cf1ef49 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 25 Jun 2019 14:02:49 -0400 Subject: Update c/storage to 67bf6c9b41967780f4bcb5725c00283ad6397679 Pick up caching of overlay feature checks Signed-off-by: Matthew Heon --- vendor.conf | 2 +- .../containers/storage/drivers/aufs/aufs.go | 22 ++-- .../containers/storage/drivers/btrfs/btrfs.go | 10 +- .../containers/storage/drivers/devmapper/driver.go | 8 +- .../containers/storage/drivers/driver.go | 15 +-- .../containers/storage/drivers/overlay/overlay.go | 124 +++++++++++++++++---- .../containers/storage/drivers/vfs/driver.go | 8 +- .../containers/storage/drivers/windows/windows.go | 4 +- .../containers/storage/drivers/zfs/zfs.go | 10 +- vendor/github.com/containers/storage/store.go | 1 + 10 files changed, 146 insertions(+), 58 deletions(-) diff --git a/vendor.conf b/vendor.conf index c9af75719..5f5b8090a 100644 --- a/vendor.conf +++ b/vendor.conf @@ -12,7 +12,7 @@ github.com/containerd/continuity master github.com/containernetworking/cni v0.7.0-alpha1 github.com/containernetworking/plugins 1562a1e60ed101aacc5e08ed9dbeba8e9f3d4ec1 github.com/containers/image 93bced01015eb94bec4821df1876314be8197680 -github.com/containers/storage 06b6c2e4cf254f5922a79da058c94ac2a65bb92f +github.com/containers/storage 67bf6c9b41967780f4bcb5725c00283ad6397679 https://github.com/mheon/storage.git github.com/containers/psgo dc0bc9fac5b715034c4310ed4d795b3182360842 github.com/coreos/go-systemd v14 github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c diff --git a/vendor/github.com/containers/storage/drivers/aufs/aufs.go b/vendor/github.com/containers/storage/drivers/aufs/aufs.go index e821bc0c5..3b4562bdd 100644 --- a/vendor/github.com/containers/storage/drivers/aufs/aufs.go +++ b/vendor/github.com/containers/storage/drivers/aufs/aufs.go @@ -83,7 +83,7 @@ type Driver struct { // Init returns a new AUFS driver. // An error is returned if AUFS is not supported. -func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { // Try to load the aufs kernel module if err := supportsAufs(); err != nil { @@ -91,7 +91,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } - fsMagic, err := graphdriver.GetFSMagic(root) + fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { return nil, err } @@ -106,7 +106,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } var mountOptions string - for _, option := range options { + for _, option := range options.DriverOptions { key, val, err := parsers.ParseKeyValueOpt(option) if err != nil { return nil, err @@ -126,36 +126,36 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } a := &Driver{ - root: root, - uidMaps: uidMaps, - gidMaps: gidMaps, + root: home, + uidMaps: options.UIDMaps, + gidMaps: options.GIDMaps, pathCache: make(map[string]string), ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicAufs)), locker: locker.New(), mountOptions: mountOptions, } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) if err != nil { return nil, err } // Create the root aufs driver dir and return // if it already exists // If not populate the dir structure - if err := idtools.MkdirAllAs(root, 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAs(home, 0700, rootUID, rootGID); err != nil { if os.IsExist(err) { return a, nil } return nil, err } - if err := mountpk.MakePrivate(root); err != nil { + if err := mountpk.MakePrivate(home); err != nil { return nil, err } // Populate the dir structure for _, p := range paths { - if err := idtools.MkdirAllAs(path.Join(root, p), 0700, rootUID, rootGID); err != nil { + if err := idtools.MkdirAllAs(path.Join(home, p), 0700, rootUID, rootGID); err != nil { return nil, err } } @@ -165,7 +165,7 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap }) for _, path := range []string{"mnt", "diff"} { - p := filepath.Join(root, path) + p := filepath.Join(home, path) entries, err := ioutil.ReadDir(p) if err != nil { logger.WithError(err).WithField("dir", p).Error("error reading dir entries") diff --git a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go index 30254d9fb..6f632a98d 100644 --- a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go +++ b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go @@ -49,7 +49,7 @@ type btrfsOptions struct { // Init returns a new BTRFS driver. // An error is returned if BTRFS is not supported. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { fsMagic, err := graphdriver.GetFSMagic(home) if err != nil { @@ -60,7 +60,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, errors.Wrapf(graphdriver.ErrPrerequisites, "%q is not on a btrfs filesystem", home) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) if err != nil { return nil, err } @@ -72,15 +72,15 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } - opt, userDiskQuota, err := parseOptions(options) + opt, userDiskQuota, err := parseOptions(options.DriverOptions) if err != nil { return nil, err } driver := &Driver{ home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, + uidMaps: options.UIDMaps, + gidMaps: options.GIDMaps, options: opt, } diff --git a/vendor/github.com/containers/storage/drivers/devmapper/driver.go b/vendor/github.com/containers/storage/drivers/devmapper/driver.go index 13677c93a..f384a6242 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/driver.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/driver.go @@ -34,8 +34,8 @@ type Driver struct { } // Init creates a driver with the given home and the set of options. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { - deviceSet, err := NewDeviceSet(home, true, options, uidMaps, gidMaps) +func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { + deviceSet, err := NewDeviceSet(home, true, options.DriverOptions, options.UIDMaps, options.GIDMaps) if err != nil { return nil, err } @@ -47,8 +47,8 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d := &Driver{ DeviceSet: deviceSet, home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, + uidMaps: options.UIDMaps, + gidMaps: options.GIDMaps, ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), locker: locker.New(), } diff --git a/vendor/github.com/containers/storage/drivers/driver.go b/vendor/github.com/containers/storage/drivers/driver.go index dda172574..ac4775f76 100644 --- a/vendor/github.com/containers/storage/drivers/driver.go +++ b/vendor/github.com/containers/storage/drivers/driver.go @@ -53,7 +53,7 @@ type MountOpts struct { } // InitFunc initializes the storage driver. -type InitFunc func(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) +type InitFunc func(homedir string, options Options) (Driver, error) // ProtoDriver defines the basic capabilities of a driver. // This interface exists solely to be a minimum set of methods @@ -202,7 +202,7 @@ func Register(name string, initFunc InitFunc) error { // GetDriver initializes and returns the registered driver func GetDriver(name string, config Options) (Driver, error) { if initFunc, exists := drivers[name]; exists { - return initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.UIDMaps, config.GIDMaps) + return initFunc(filepath.Join(config.Root, name), config) } logrus.Errorf("Failed to GetDriver graph %s %s", name, config.Root) @@ -210,9 +210,9 @@ func GetDriver(name string, config Options) (Driver, error) { } // getBuiltinDriver initializes and returns the registered driver, but does not try to load from plugins -func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) { +func getBuiltinDriver(name, home string, options Options) (Driver, error) { if initFunc, exists := drivers[name]; exists { - return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps) + return initFunc(filepath.Join(home, name), options) } logrus.Errorf("Failed to built-in GetDriver graph %s %s", name, home) return nil, errors.Wrapf(ErrNotSupported, "failed to built-in GetDriver graph %s %s", name, home) @@ -221,6 +221,7 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id // Options is used to initialize a graphdriver type Options struct { Root string + RunRoot string DriverOptions []string UIDMaps []idtools.IDMap GIDMaps []idtools.IDMap @@ -244,7 +245,7 @@ func New(name string, config Options) (Driver, error) { if _, prior := driversMap[name]; prior { // of the state found from prior drivers, check in order of our priority // which we would prefer - driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps) + driver, err := getBuiltinDriver(name, config.Root, config) if err != nil { // unlike below, we will return error here, because there is prior // state, and now it is no longer supported/prereq/compatible, so @@ -272,7 +273,7 @@ func New(name string, config Options) (Driver, error) { // Check for priority drivers first for _, name := range priority { - driver, err := getBuiltinDriver(name, config.Root, config.DriverOptions, config.UIDMaps, config.GIDMaps) + driver, err := getBuiltinDriver(name, config.Root, config) if err != nil { if isDriverNotSupported(err) { continue @@ -284,7 +285,7 @@ func New(name string, config Options) (Driver, error) { // Check all registered drivers if no priority driver is found for name, initFunc := range drivers { - driver, err := initFunc(filepath.Join(config.Root, name), config.DriverOptions, config.UIDMaps, config.GIDMaps) + driver, err := initFunc(filepath.Join(config.Root, name), config) if err != nil { if isDriverNotSupported(err) { continue diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 57d6dd63a..262734d31 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -97,6 +97,7 @@ type overlayOptions struct { type Driver struct { name string home string + runhome string uidMaps []idtools.IDMap gidMaps []idtools.IDMap ctr *graphdriver.RefCounter @@ -125,8 +126,8 @@ func init() { // Init returns the a native diff driver for overlay filesystem. // If overlay filesystem is not supported on the host, a wrapped graphdriver.ErrNotSupported is returned as error. // If an overlay filesystem is not supported over an existing filesystem then a wrapped graphdriver.ErrIncompatibleFS is returned. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { - opts, err := parseOptions(options) +func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { + opts, err := parseOptions(options.DriverOptions) if err != nil { return nil, err } @@ -148,7 +149,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) if err != nil { return nil, err } @@ -157,32 +158,72 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap if err := idtools.MkdirAllAs(path.Join(home, linkDir), 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { return nil, err } + runhome := filepath.Join(options.RunRoot, filepath.Base(home)) + if err := idtools.MkdirAllAs(runhome, 0700, rootUID, rootGID); err != nil && !os.IsExist(err) { + return nil, err + } var usingMetacopy bool var supportsDType bool if opts.mountProgram != "" { supportsDType = true } else { - supportsDType, err = supportsOverlay(home, fsMagic, rootUID, rootGID) - if err != nil { - os.Remove(filepath.Join(home, linkDir)) - os.Remove(home) - patherr, ok := err.(*os.PathError) - if ok && patherr.Err == syscall.ENOSPC { + feature := "overlay" + overlayCacheResult, overlayCacheText, err := cachedFeatureCheck(runhome, feature) + if err == nil { + if overlayCacheResult { + logrus.Debugf("cached value indicated that overlay is supported") + } else { + logrus.Debugf("cached value indicated that overlay is not supported") + } + supportsDType = overlayCacheResult + if !supportsDType { + return nil, errors.New(overlayCacheText) + } + } else { + supportsDType, err = supportsOverlay(home, fsMagic, rootUID, rootGID) + if err != nil { + os.Remove(filepath.Join(home, linkDir)) + os.Remove(home) + patherr, ok := err.(*os.PathError) + if ok && patherr.Err == syscall.ENOSPC { + return nil, err + } + err = errors.Wrap(err, "kernel does not support overlay fs") + if err2 := cachedFeatureRecord(runhome, feature, false, err.Error()); err2 != nil { + return nil, errors.Wrapf(err2, "error recording overlay not being supported (%v)", err) + } return nil, err } - return nil, errors.Wrap(err, "kernel does not support overlay fs") + if err = cachedFeatureRecord(runhome, feature, supportsDType, ""); err != nil { + return nil, errors.Wrap(err, "error recording overlay support status") + } } - usingMetacopy, err = doesMetacopy(home, opts.mountOptions) + + feature = fmt.Sprintf("metacopy(%s)", opts.mountOptions) + metacopyCacheResult, _, err := cachedFeatureCheck(runhome, feature) if err == nil { - if usingMetacopy { - logrus.Debugf("overlay test mount indicated that metacopy is being used") + if metacopyCacheResult { + logrus.Debugf("cached value indicated that metacopy is being used") } else { - logrus.Debugf("overlay test mount indicated that metacopy is not being used") + logrus.Debugf("cached value indicated that metacopy is not being used") } + usingMetacopy = metacopyCacheResult } else { - logrus.Warnf("overlay test mount did not indicate whether or not metacopy is being used: %v", err) - return nil, err + usingMetacopy, err = doesMetacopy(home, opts.mountOptions) + if err == nil { + if usingMetacopy { + logrus.Debugf("overlay test mount indicated that metacopy is being used") + } else { + logrus.Debugf("overlay test mount indicated that metacopy is not being used") + } + if err = cachedFeatureRecord(runhome, feature, usingMetacopy, ""); err != nil { + return nil, errors.Wrap(err, "error recording metacopy-being-used status") + } + } else { + logrus.Warnf("overlay test mount did not indicate whether or not metacopy is being used: %v", err) + return nil, err + } } } @@ -201,8 +242,9 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap d := &Driver{ name: "overlay", home: home, - uidMaps: uidMaps, - gidMaps: gidMaps, + runhome: runhome, + uidMaps: options.UIDMaps, + gidMaps: options.GIDMaps, ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)), supportsDType: supportsDType, usingMetacopy: usingMetacopy, @@ -291,6 +333,36 @@ func parseOptions(options []string) (*overlayOptions, error) { return o, nil } +func cachedFeatureSet(feature string, set bool) string { + if set { + return fmt.Sprintf("%s-true", feature) + } + return fmt.Sprintf("%s-false", feature) +} + +func cachedFeatureCheck(runhome, feature string) (supported bool, text string, err error) { + content, err := ioutil.ReadFile(filepath.Join(runhome, cachedFeatureSet(feature, true))) + if err == nil { + return true, string(content), nil + } + content, err = ioutil.ReadFile(filepath.Join(runhome, cachedFeatureSet(feature, false))) + if err == nil { + return false, string(content), nil + } + return false, "", err +} + +func cachedFeatureRecord(runhome, feature string, supported bool, text string) (err error) { + f, err := os.Create(filepath.Join(runhome, cachedFeatureSet(feature, supported))) + if f != nil { + if text != "" { + fmt.Fprintf(f, "%s", text) + } + f.Close() + } + return err +} + func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGID int) (supportsDType bool, err error) { // We can try to modprobe overlay first @@ -363,10 +435,24 @@ func (d *Driver) useNaiveDiff() bool { useNaiveDiffOnly = true return } + feature := fmt.Sprintf("native-diff(%s)", d.options.mountOptions) + nativeDiffCacheResult, nativeDiffCacheText, err := cachedFeatureCheck(d.runhome, feature) + if err == nil { + if nativeDiffCacheResult { + logrus.Debugf("cached value indicated that native-diff is usable") + } else { + logrus.Debugf("cached value indicated that native-diff is not being used") + logrus.Warn(nativeDiffCacheText) + } + useNaiveDiffOnly = !nativeDiffCacheResult + return + } if err := doesSupportNativeDiff(d.home, d.options.mountOptions); err != nil { - logrus.Warnf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err) + nativeDiffCacheText = fmt.Sprintf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err) + logrus.Warn(nativeDiffCacheText) useNaiveDiffOnly = true } + cachedFeatureRecord(d.runhome, feature, !useNaiveDiffOnly, nativeDiffCacheText) }) return useNaiveDiffOnly } diff --git a/vendor/github.com/containers/storage/drivers/vfs/driver.go b/vendor/github.com/containers/storage/drivers/vfs/driver.go index 5941ccc17..8b981632a 100644 --- a/vendor/github.com/containers/storage/drivers/vfs/driver.go +++ b/vendor/github.com/containers/storage/drivers/vfs/driver.go @@ -24,16 +24,16 @@ func init() { // Init returns a new VFS driver. // This sets the home directory for the driver and returns NaiveDiffDriver. -func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(home string, options graphdriver.Options) (graphdriver.Driver, error) { d := &Driver{ homes: []string{home}, - idMappings: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), + idMappings: idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps), } rootIDs := d.idMappings.RootPair() if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil { return nil, err } - for _, option := range options { + for _, option := range options.DriverOptions { if strings.HasPrefix(option, "vfs.imagestore=") { d.homes = append(d.homes, strings.Split(option[15:], ",")...) continue @@ -59,7 +59,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } } if d.ostreeRepo != "" { - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + rootUID, rootGID, err := idtools.GetRootUIDGID(options.UIDMaps, options.GIDMaps) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/storage/drivers/windows/windows.go b/vendor/github.com/containers/storage/drivers/windows/windows.go index c7df1c1fe..f0437a275 100644 --- a/vendor/github.com/containers/storage/drivers/windows/windows.go +++ b/vendor/github.com/containers/storage/drivers/windows/windows.go @@ -83,10 +83,10 @@ type Driver struct { } // InitFilter returns a new Windows storage filter driver. -func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func InitFilter(home string, options graphdriver.Options) (graphdriver.Driver, error) { logrus.Debugf("WindowsGraphDriver InitFilter at %s", home) - for _, option := range options { + for _, option := range options.DriverOptions { if strings.HasPrefix(option, "windows.mountopt=") { return nil, fmt.Errorf("windows driver does not support mount options") } else { diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs.go b/vendor/github.com/containers/storage/drivers/zfs/zfs.go index eaa9e8bc5..a2bf5565b 100644 --- a/vendor/github.com/containers/storage/drivers/zfs/zfs.go +++ b/vendor/github.com/containers/storage/drivers/zfs/zfs.go @@ -44,7 +44,7 @@ func (*Logger) Log(cmd []string) { // Init returns a new ZFS driver. // It takes base mount path and an array of options which are represented as key value pairs. // Each option is in the for key=value. 'zfs.fsname' is expected to be a valid key in the options. -func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { +func Init(base string, opt graphdriver.Options) (graphdriver.Driver, error) { var err error logger := logrus.WithField("storage-driver", "zfs") @@ -61,7 +61,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri } defer file.Close() - options, err := parseOptions(opt) + options, err := parseOptions(opt.DriverOptions) if err != nil { return nil, err } @@ -103,7 +103,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + rootUID, rootGID, err := idtools.GetRootUIDGID(opt.UIDMaps, opt.GIDMaps) if err != nil { return nil, fmt.Errorf("Failed to get root uid/guid: %v", err) } @@ -115,8 +115,8 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri dataset: rootDataset, options: options, filesystemsCache: filesystemsCache, - uidMaps: uidMaps, - gidMaps: gidMaps, + uidMaps: opt.UIDMaps, + gidMaps: opt.GIDMaps, ctr: graphdriver.NewRefCounter(graphdriver.NewDefaultChecker()), } return graphdriver.NewNaiveDiffDriver(d, graphdriver.NewNaiveLayerIDMapUpdater(d)), nil diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index 856c73e51..acc22be27 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -695,6 +695,7 @@ func (s *store) getGraphDriver() (drivers.Driver, error) { } config := drivers.Options{ Root: s.graphRoot, + RunRoot: s.runRoot, DriverOptions: s.graphOptions, UIDMaps: s.uidMap, GIDMaps: s.gidMap, -- cgit v1.2.3-54-g00ecf