Skip to content

Commit

Permalink
layers: write read only layers to imagestore
Browse files Browse the repository at this point in the history
when an imagestore is used, a R/O layer must be written to the
layers.json under the imagestore, not graphroot.

The lock on the imagestore is already taken through the
multipleLockFile{} mechanism in place.

Closes: containers#2257

Signed-off-by: Giuseppe Scrivano <[email protected]>
  • Loading branch information
giuseppe committed Feb 14, 2025
1 parent b6f6fb2 commit 73bde82
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 0 deletions.
18 changes: 18 additions & 0 deletions layers.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type layerLocations uint8
// unclean shutdown
const (
stableLayerLocation layerLocations = 1 << iota
imageStoreLayerLocation
volatileLayerLocation

numLayerLocationIndex = iota
Expand Down Expand Up @@ -167,6 +168,9 @@ type Layer struct {
// volatileStore is true if the container is from the volatile json file
volatileStore bool `json:"-"`

// imageStore is true if the layer is from the image store json file
imageStore bool `json:"-"`

// BigDataNames is a list of names of data items that we keep for the
// convenience of the caller. They can be large, and are only in
// memory when being read from or written to disk.
Expand Down Expand Up @@ -435,6 +439,9 @@ func layerLocation(l *Layer) layerLocations {
if l.volatileStore {
return volatileLayerLocation
}
if l.imageStore {
return imageStoreLayerLocation
}
return stableLayerLocation
}

Expand All @@ -456,6 +463,7 @@ func copyLayer(l *Layer) *Layer {
CompressionType: l.CompressionType,
ReadOnly: l.ReadOnly,
volatileStore: l.volatileStore,
imageStore: l.imageStore,
BigDataNames: copySlicePreferringNil(l.BigDataNames),
Flags: copyMapPreferringNil(l.Flags),
UIDMap: copySlicePreferringNil(l.UIDMap),
Expand Down Expand Up @@ -823,6 +831,9 @@ func (r *layerStore) load(lockedForWriting bool) (bool, error) {
if location == volatileLayerLocation {
layer.volatileStore = true
}
if location == imageStoreLayerLocation {
layer.imageStore = true
}
layers = append(layers, layer)
ids[layer.ID] = layer
}
Expand Down Expand Up @@ -1144,6 +1155,7 @@ func (s *store) newLayerStore(rundir, layerdir, imagedir string, driver drivers.
rundir: rundir,
jsonPath: [numLayerLocationIndex]string{
filepath.Join(layerdir, "layers.json"),
filepath.Join(imagedir, "layers.json"),
filepath.Join(volatileDir, "volatile-layers.json"),
},
layerdir: layerdir,
Expand Down Expand Up @@ -1180,6 +1192,7 @@ func newROLayerStore(rundir string, layerdir string, driver drivers.Driver) (roL
mountsLockfile: nil,
rundir: rundir,
jsonPath: [numLayerLocationIndex]string{
filepath.Join(layerdir, "layers.json"),
filepath.Join(layerdir, "layers.json"),
filepath.Join(layerdir, "volatile-layers.json"),
},
Expand Down Expand Up @@ -1399,6 +1412,10 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
selinux.ReserveLabel(mountLabel)
}

if moreOptions.Volatile && moreOptions.ImageStore {
return nil, -1, errors.New("internal error: volatile and image store layers are mutually exclusive")
}

// Before actually creating the layer, make a persistent record of it
// with the incomplete flag set, so that future processes have a chance
// to clean up after it.
Expand All @@ -1422,6 +1439,7 @@ func (r *layerStore) create(id string, parentLayer *Layer, names []string, mount
GIDMap: copySlicePreferringNil(moreOptions.GIDMap),
BigDataNames: []string{},
volatileStore: moreOptions.Volatile,
imageStore: moreOptions.ImageStore,
}
layer.Flags[incompleteFlag] = true

Expand Down
5 changes: 5 additions & 0 deletions store.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,8 @@ type LayerOptions struct {
UncompressedDigest digest.Digest
// True is the layer info can be treated as volatile
Volatile bool
// True is the layer info must be written to the image store
ImageStore bool
// BigData is a set of items which should be stored with the layer.
BigData []LayerBigDataOption
// Flags is a set of named flags and their values to store with the layer.
Expand Down Expand Up @@ -1545,6 +1547,9 @@ func (s *store) putLayer(rlstore rwLayerStore, rlstores []roLayerStore, id, pare
GIDMap: copySlicePreferringNil(gidMap),
}
}
if !writeable && s.imageStoreDir != "" {
options.ImageStore = true
}
return rlstore.create(id, parentLayer, names, mountLabel, nil, &options, writeable, diff, slo)
}

Expand Down
11 changes: 11 additions & 0 deletions tests/split-store.bats
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,17 @@ load helpers
# shutdown store
run storage --graph ${TESTDIR}/graph --image-store ${TESTDIR}/imagestore/ --run ${TESTDIR}/runroot/ shutdown

# A RO layer must be created in the image store and must be usable from there as a regular store.
run storage --graph ${TESTDIR}/graph --image-store ${TESTDIR}/imagestore/ --debug=false create-layer --readonly
[ "$status" -eq 0 ]
rolayer=$output
run storage --graph ${TESTDIR}/imagestore --debug=false mount $rolayer
[ "$status" -eq 0 ]
run storage --graph ${TESTDIR}/imagestore --debug=false unmount $rolayer
[ "$status" -eq 0 ]
run storage --graph ${TESTDIR}/imagestore shutdown
[ "$status" -eq 0 ]

# Now since image was deleted from graphRoot, we should
# get false output while checking if image still exists
run storage --graph ${TESTDIR}/graph exists -i $image
Expand Down

0 comments on commit 73bde82

Please sign in to comment.