Modify storage to allow callers to determine if a mount point is mounted
Add force to umount to force the umount of a container image Add an interface to indicate whether or not the layer is mounted Add a boolean return from unmount to indicate when the layer is really unmounted Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
124de6810e
commit
1075a73cac
|
|
@ -59,7 +59,7 @@ func copyContent(flags *mflag.FlagSet, action string, m storage.Store, args []st
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
target = filepath.Join(targetMount, targetParts[1])
|
target = filepath.Join(targetMount, targetParts[1])
|
||||||
defer m.Unmount(targetLayer.ID)
|
defer m.Unmount(targetLayer.ID, false)
|
||||||
}
|
}
|
||||||
args = args[:len(args)-1]
|
args = args[:len(args)-1]
|
||||||
for _, srcSpec := range args {
|
for _, srcSpec := range args {
|
||||||
|
|
@ -83,7 +83,7 @@ func copyContent(flags *mflag.FlagSet, action string, m storage.Store, args []st
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
source = filepath.Join(sourceMount, sourceParts[1])
|
source = filepath.Join(sourceMount, sourceParts[1])
|
||||||
defer m.Unmount(sourceLayer.ID)
|
defer m.Unmount(sourceLayer.ID, false)
|
||||||
}
|
}
|
||||||
archiver := chrootarchive.NewArchiverWithChown(tarIDMappings, chownOpts, untarIDMappings)
|
archiver := chrootarchive.NewArchiverWithChown(tarIDMappings, chownOpts, untarIDMappings)
|
||||||
if err := archiver.CopyWithTar(source, target); err != nil {
|
if err := archiver.CopyWithTar(source, target); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type command struct {
|
||||||
var (
|
var (
|
||||||
commands = []command{}
|
commands = []command{}
|
||||||
jsonOutput = false
|
jsonOutput = false
|
||||||
|
force = false
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,15 @@ func unmount(flags *mflag.FlagSet, action string, m storage.Store, args []string
|
||||||
mes := []mountPointError{}
|
mes := []mountPointError{}
|
||||||
errors := false
|
errors := false
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
err := m.Unmount(arg)
|
mounted, err := m.Unmount(arg, force)
|
||||||
errText := ""
|
errText := ""
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errText = fmt.Sprintf("%v", err)
|
errText = fmt.Sprintf("%v", err)
|
||||||
errors = true
|
errors = true
|
||||||
|
} else {
|
||||||
|
if !mounted {
|
||||||
|
fmt.Printf("%s mountpoint unmounted\n", arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mes = append(mes, mountPointError{arg, errText})
|
mes = append(mes, mountPointError{arg, errText})
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +78,37 @@ func unmount(flags *mflag.FlagSet, action string, m storage.Store, args []string
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mounted(flags *mflag.FlagSet, action string, m storage.Store, args []string) int {
|
||||||
|
mes := []mountPointError{}
|
||||||
|
errors := false
|
||||||
|
for _, arg := range args {
|
||||||
|
mounted, err := m.Mounted(arg)
|
||||||
|
errText := ""
|
||||||
|
if err != nil {
|
||||||
|
errText = fmt.Sprintf("%v", err)
|
||||||
|
errors = true
|
||||||
|
} else {
|
||||||
|
if mounted {
|
||||||
|
fmt.Printf("%s mounted\n", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mes = append(mes, mountPointError{arg, errText})
|
||||||
|
}
|
||||||
|
if jsonOutput {
|
||||||
|
json.NewEncoder(os.Stdout).Encode(mes)
|
||||||
|
} else {
|
||||||
|
for _, me := range mes {
|
||||||
|
if me.Error != "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s checking if %s is mounted\n", me.Error, me.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errors {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
commands = append(commands, command{
|
commands = append(commands, command{
|
||||||
names: []string{"mount"},
|
names: []string{"mount"},
|
||||||
|
|
@ -92,6 +127,17 @@ func init() {
|
||||||
usage: "Unmount a layer or container",
|
usage: "Unmount a layer or container",
|
||||||
minArgs: 1,
|
minArgs: 1,
|
||||||
action: unmount,
|
action: unmount,
|
||||||
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
|
flags.BoolVar(&force, []string{"-force", "f"}, jsonOutput, "Force the umount")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
commands = append(commands, command{
|
||||||
|
names: []string{"mounted"},
|
||||||
|
optionsHelp: "LayerOrContainerNameOrID",
|
||||||
|
usage: "Check if a file system is mounted",
|
||||||
|
minArgs: 1,
|
||||||
|
action: mounted,
|
||||||
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
},
|
},
|
||||||
|
|
|
||||||
35
layers.go
35
layers.go
|
|
@ -211,7 +211,10 @@ type LayerStore interface {
|
||||||
Mount(id, mountLabel string) (string, error)
|
Mount(id, mountLabel string) (string, error)
|
||||||
|
|
||||||
// Unmount unmounts a layer when it is no longer in use.
|
// Unmount unmounts a layer when it is no longer in use.
|
||||||
Unmount(id string) error
|
Unmount(id string, force bool) (bool, error)
|
||||||
|
|
||||||
|
// Mounted returns whether or not the layer is mounted.
|
||||||
|
Mounted(id string) (bool, error)
|
||||||
|
|
||||||
// ParentOwners returns the UIDs and GIDs of parents of the layer's mountpoint
|
// ParentOwners returns the UIDs and GIDs of parents of the layer's mountpoint
|
||||||
// for which the layer's UID and GID maps don't contain corresponding entries.
|
// for which the layer's UID and GID maps don't contain corresponding entries.
|
||||||
|
|
@ -624,6 +627,14 @@ func (r *layerStore) Create(id string, parent *Layer, names []string, mountLabel
|
||||||
return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil)
|
return r.CreateWithFlags(id, parent, names, mountLabel, options, moreOptions, writeable, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *layerStore) Mounted(id string) (bool, error) {
|
||||||
|
layer, ok := r.lookup(id)
|
||||||
|
if !ok {
|
||||||
|
return false, ErrLayerUnknown
|
||||||
|
}
|
||||||
|
return layer.MountCount > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
||||||
if !r.IsReadWrite() {
|
if !r.IsReadWrite() {
|
||||||
return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
|
return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
|
||||||
|
|
@ -652,21 +663,24 @@ func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
||||||
return mountpoint, err
|
return mountpoint, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) Unmount(id string) error {
|
func (r *layerStore) Unmount(id string, force bool) (bool, error) {
|
||||||
if !r.IsReadWrite() {
|
if !r.IsReadWrite() {
|
||||||
return errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
|
return false, errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
|
||||||
}
|
}
|
||||||
layer, ok := r.lookup(id)
|
layer, ok := r.lookup(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrLayerUnknown
|
return false, ErrLayerUnknown
|
||||||
}
|
}
|
||||||
layer = layerByMount
|
layer = layerByMount
|
||||||
}
|
}
|
||||||
|
if force {
|
||||||
|
layer.MountCount = 1
|
||||||
|
}
|
||||||
if layer.MountCount > 1 {
|
if layer.MountCount > 1 {
|
||||||
layer.MountCount--
|
layer.MountCount--
|
||||||
return r.Save()
|
return true, r.Save()
|
||||||
}
|
}
|
||||||
err := r.driver.Put(id)
|
err := r.driver.Put(id)
|
||||||
if err == nil || os.IsNotExist(err) {
|
if err == nil || os.IsNotExist(err) {
|
||||||
|
|
@ -675,9 +689,9 @@ func (r *layerStore) Unmount(id string) error {
|
||||||
}
|
}
|
||||||
layer.MountCount--
|
layer.MountCount--
|
||||||
layer.MountPoint = ""
|
layer.MountPoint = ""
|
||||||
err = r.Save()
|
return false, r.Save()
|
||||||
}
|
}
|
||||||
return err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) {
|
func (r *layerStore) ParentOwners(id string) (uids, gids []int, err error) {
|
||||||
|
|
@ -797,11 +811,9 @@ func (r *layerStore) Delete(id string) error {
|
||||||
return ErrLayerUnknown
|
return ErrLayerUnknown
|
||||||
}
|
}
|
||||||
id = layer.ID
|
id = layer.ID
|
||||||
for layer.MountCount > 0 {
|
if _, err := r.Unmount(id, true); err != nil {
|
||||||
if err := r.Unmount(id); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
err := r.driver.Remove(id)
|
err := r.driver.Remove(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
os.Remove(r.tspath(id))
|
os.Remove(r.tspath(id))
|
||||||
|
|
@ -917,7 +929,8 @@ func (s *simpleGetCloser) Get(path string) (io.ReadCloser, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *simpleGetCloser) Close() error {
|
func (s *simpleGetCloser) Close() error {
|
||||||
return s.r.Unmount(s.id)
|
_, err := s.r.Unmount(s.id, false)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
|
func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
|
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
|
||||||
// source: layers.go
|
// source: ./layers.go
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
|
|
|
||||||
34
store.go
34
store.go
|
|
@ -262,8 +262,11 @@ type Store interface {
|
||||||
Mount(id, mountLabel string) (string, error)
|
Mount(id, mountLabel string) (string, error)
|
||||||
|
|
||||||
// Unmount attempts to unmount a layer, image, or container, given an ID, a
|
// Unmount attempts to unmount a layer, image, or container, given an ID, a
|
||||||
// name, or a mount path.
|
// name, or a mount path. Returns whether or not the layer is still mounted.
|
||||||
Unmount(id string) error
|
Unmount(id string, force bool) (bool, error)
|
||||||
|
|
||||||
|
// Unmount attempts to discover whether the specified id is mounted.
|
||||||
|
Mounted(id string) (bool, error)
|
||||||
|
|
||||||
// Changes returns a summary of the changes which would need to be made
|
// Changes returns a summary of the changes which would need to be made
|
||||||
// to one layer to make its contents the same as a second layer. If
|
// to one layer to make its contents the same as a second layer. If
|
||||||
|
|
@ -2245,13 +2248,30 @@ func (s *store) Mount(id, mountLabel string) (string, error) {
|
||||||
return "", ErrLayerUnknown
|
return "", ErrLayerUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) Unmount(id string) error {
|
func (s *store) Mounted(id string) (bool, error) {
|
||||||
if layerID, err := s.ContainerLayerID(id); err == nil {
|
if layerID, err := s.ContainerLayerID(id); err == nil {
|
||||||
id = layerID
|
id = layerID
|
||||||
}
|
}
|
||||||
rlstore, err := s.LayerStore()
|
rlstore, err := s.LayerStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
|
}
|
||||||
|
rlstore.Lock()
|
||||||
|
defer rlstore.Unlock()
|
||||||
|
if modified, err := rlstore.Modified(); modified || err != nil {
|
||||||
|
rlstore.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
return rlstore.Mounted(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *store) Unmount(id string, force bool) (bool, error) {
|
||||||
|
if layerID, err := s.ContainerLayerID(id); err == nil {
|
||||||
|
id = layerID
|
||||||
|
}
|
||||||
|
rlstore, err := s.LayerStore()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
}
|
}
|
||||||
rlstore.Lock()
|
rlstore.Lock()
|
||||||
defer rlstore.Unlock()
|
defer rlstore.Unlock()
|
||||||
|
|
@ -2259,9 +2279,9 @@ func (s *store) Unmount(id string) error {
|
||||||
rlstore.Load()
|
rlstore.Load()
|
||||||
}
|
}
|
||||||
if rlstore.Exists(id) {
|
if rlstore.Exists(id) {
|
||||||
return rlstore.Unmount(id)
|
return rlstore.Unmount(id, force)
|
||||||
}
|
}
|
||||||
return ErrLayerUnknown
|
return false, ErrLayerUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) Changes(from, to string) ([]archive.Change, error) {
|
func (s *store) Changes(from, to string) ([]archive.Change, error) {
|
||||||
|
|
@ -2811,7 +2831,7 @@ func (s *store) Shutdown(force bool) ([]string, error) {
|
||||||
mounted = append(mounted, layer.ID)
|
mounted = append(mounted, layer.ID)
|
||||||
if force {
|
if force {
|
||||||
for layer.MountCount > 0 {
|
for layer.MountCount > 0 {
|
||||||
err2 := rlstore.Unmount(layer.ID)
|
_, err2 := rlstore.Unmount(layer.ID, force)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = err2
|
err = err2
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "mount-layer" {
|
||||||
|
# Create a layer.
|
||||||
|
run storage --debug=false create-layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
layer="$output"
|
||||||
|
|
||||||
|
# Mount the layer.
|
||||||
|
run storage --debug=false mount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
# Check if layer is mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "$layer mounted" ]
|
||||||
|
# Unmount the layer.
|
||||||
|
run storage --debug=false unmount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
# Make sure layer is not mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "" ]
|
||||||
|
|
||||||
|
# Mount the layer twice.
|
||||||
|
run storage --debug=false mount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
run storage --debug=false mount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
# Check if layer is mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "$layer mounted" ]
|
||||||
|
# Unmount the second layer.
|
||||||
|
run storage --debug=false unmount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "" ]
|
||||||
|
# Check if layer is mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "$layer mounted" ]
|
||||||
|
# Unmount the first layer.
|
||||||
|
run storage --debug=false unmount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
# Make sure layer is not mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "" ]
|
||||||
|
|
||||||
|
|
||||||
|
# Mount the layer twice and force umount.
|
||||||
|
run storage --debug=false mount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
run storage --debug=false mount $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
# Check if layer is mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "$layer mounted" ]
|
||||||
|
# Unmount all layers.
|
||||||
|
run storage --debug=false unmount --force $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" != "" ]
|
||||||
|
# Make sure no layers are mounted.
|
||||||
|
run storage --debug=false mounted $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" == "" ]
|
||||||
|
|
||||||
|
# Delete the first layer
|
||||||
|
run storage delete-layer $layer
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue