Skip to content

Commit

Permalink
Merge pull request #2061 from Honny1/store-list-layers-images-containers
Browse files Browse the repository at this point in the history
Create `Store.List` method for listing layers, images, containers
  • Loading branch information
openshift-merge-bot[bot] authored Aug 20, 2024
2 parents 8743682 + b175ede commit e5de483
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 31 deletions.
72 changes: 72 additions & 0 deletions store.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ type ApplyStagedLayerOptions struct {
DiffOptions *drivers.ApplyDiffWithDifferOpts // Mandatory
}

// MultiListOptions contains options to pass to MultiList
type MultiListOptions struct {
Images bool // if true, Images will be listed in the result
Layers bool // if true, layers will be listed in the result
Containers bool // if true, containers will be listed in the result
}

// MultiListResult contains slices of Images, Layers or Containers listed by MultiList method
type MultiListResult struct {
Images []Image
Layers []Layer
Containers []Container
}

// An roBigDataStore wraps up the read-only big-data related methods of the
// various types of file-based lookaside stores that we implement.
type roBigDataStore interface {
Expand Down Expand Up @@ -561,6 +575,12 @@ type Store interface {
// usually by deleting layers and images which are damaged. If the
// right options are set, it will remove containers as well.
Repair(report CheckReport, options *RepairOptions) []error

// MultiList returns a MultiListResult structure that contains layer, image, or container
// extracts according to the values in MultiListOptions.
// MultiList returns consistent values as of a single point in time.
// WARNING: The values may already be out of date by the time they are returned to the caller.
MultiList(MultiListOptions) (MultiListResult, error)
}

// AdditionalLayer represents a layer that is contained in the additional layer store
Expand Down Expand Up @@ -3815,3 +3835,55 @@ func (s *store) GarbageCollect() error {

return firstErr
}

// List returns a MultiListResult structure that contains layer, image, or container
// extracts according to the values in MultiListOptions.
func (s *store) MultiList(options MultiListOptions) (MultiListResult, error) {
// TODO: Possible optimization: Deduplicate content from multiple stores.
out := MultiListResult{}

if options.Layers {
layerStores, err := s.allLayerStores()
if err != nil {
return MultiListResult{}, err
}
for _, roStore := range layerStores {
if err := roStore.startReading(); err != nil {
return MultiListResult{}, err
}
defer roStore.stopReading()
layers, err := roStore.Layers()
if err != nil {
return MultiListResult{}, err
}
out.Layers = append(out.Layers, layers...)
}
}

if options.Images {
for _, roStore := range s.allImageStores() {
if err := roStore.startReading(); err != nil {
return MultiListResult{}, err
}
defer roStore.stopReading()

images, err := roStore.Images()
if err != nil {
return MultiListResult{}, err
}
out.Images = append(out.Images, images...)
}
}

if options.Containers {
containers, _, err := readContainerStore(s, func() ([]Container, bool, error) {
res, err := s.containerStore.Containers()
return res, true, err
})
if err != nil {
return MultiListResult{}, err
}
out.Containers = append(out.Containers, containers...)
}
return out, nil
}
146 changes: 115 additions & 31 deletions store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,53 @@ import (
"testing"

"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/reexec"
digest "github.com/opencontainers/go-digest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestStore(t *testing.T) {
func newTestStore(t *testing.T, testOptions StoreOptions) Store {
wd := t.TempDir()

pullOpts := map[string]string{"Test1": "test1", "Test2": "test2"}
store, err := GetStore(StoreOptions{
RunRoot: filepath.Join(wd, "run"),
GraphRoot: filepath.Join(wd, "root"),
GraphDriverName: "vfs",
GraphDriverOptions: []string{},
UIDMap: []idtools.IDMap{{
options := testOptions
if testOptions.RunRoot == "" {
options.RunRoot = filepath.Join(wd, "run")
}
if testOptions.GraphRoot == "" {
options.GraphRoot = filepath.Join(wd, "root")
}
if testOptions.GraphDriverName == "" {
options.GraphDriverName = "vfs"
}
if testOptions.GraphDriverOptions == nil {
options.GraphDriverOptions = []string{}
}
if len(testOptions.UIDMap) == 0 {
options.UIDMap = []idtools.IDMap{{
ContainerID: 0,
HostID: os.Getuid(),
Size: 1,
}},
GIDMap: []idtools.IDMap{{
}}
}
if len(testOptions.GIDMap) == 0 {
options.GIDMap = []idtools.IDMap{{
ContainerID: 0,
HostID: os.Getgid(),
Size: 1,
}},
}}
}

store, err := GetStore(options)
require.NoError(t, err)
return store
}

func TestStore(t *testing.T) {
pullOpts := map[string]string{"Test1": "test1", "Test2": "test2"}
store := newTestStore(t, StoreOptions{
PullOptions: pullOpts,
})
require.NoError(t, err)

root := store.RunRoot()
require.NotNil(t, root)
Expand All @@ -52,7 +72,7 @@ func TestStore(t *testing.T) {
opts := store.PullOptions()
assert.Equal(t, pullOpts, opts)

_, err = store.GraphDriver()
_, err := store.GraphDriver()
require.Nil(t, err)

_, err = store.CreateLayer("foo", "bar", nil, "", false, nil)
Expand Down Expand Up @@ -252,25 +272,10 @@ func TestWithSplitStore(t *testing.T) {
wd := t.TempDir()

pullOpts := map[string]string{"Test1": "test1", "Test2": "test2"}
store, err := GetStore(StoreOptions{
RunRoot: filepath.Join(wd, "run"),
GraphRoot: filepath.Join(wd, "root"),
ImageStore: filepath.Join(wd, "imgstore"),
GraphDriverName: "vfs",
GraphDriverOptions: []string{},
UIDMap: []idtools.IDMap{{
ContainerID: 0,
HostID: os.Getuid(),
Size: 1,
}},
GIDMap: []idtools.IDMap{{
ContainerID: 0,
HostID: os.Getgid(),
Size: 1,
}},
store := newTestStore(t, StoreOptions{
ImageStore: filepath.Join(wd, "imgstore"),
PullOptions: pullOpts,
})
require.NoError(t, err)

root := store.RunRoot()
require.NotNil(t, root)
Expand All @@ -290,7 +295,7 @@ func TestWithSplitStore(t *testing.T) {
opts := store.PullOptions()
assert.Equal(t, pullOpts, opts)

_, err = store.GraphDriver()
_, err := store.GraphDriver()
require.Nil(t, err)

_, err = store.CreateLayer("foo", "bar", nil, "", false, nil)
Expand Down Expand Up @@ -485,3 +490,82 @@ func TestWithSplitStore(t *testing.T) {
store.Free()
store.Free()
}

func TestStoreMultiList(t *testing.T) {
reexec.Init()

store := newTestStore(t, StoreOptions{})

_, err := store.CreateLayer("Layer", "", nil, "", false, nil)
require.NoError(t, err)

_, err = store.CreateImage("Image", nil, "Layer", "", nil)
require.NoError(t, err)

_, err = store.CreateContainer("Container", nil, "Image", "", "", nil)
require.NoError(t, err)

tests := []struct {
options MultiListOptions
layerCounts int
imageCounts int
containerCounts int
}{
{
options: MultiListOptions{
Layers: true,
Images: true,
Containers: true,
},
layerCounts: 3,
imageCounts: 1,
containerCounts: 1,
},

{
options: MultiListOptions{
Layers: true,
Images: false,
Containers: false,
},
layerCounts: 3,
imageCounts: 0,
containerCounts: 0,
},

{
options: MultiListOptions{
Layers: false,
Images: true,
Containers: false,
},
layerCounts: 0,
imageCounts: 1,
containerCounts: 0,
},

{
options: MultiListOptions{
Layers: false,
Images: false,
Containers: true,
},
layerCounts: 0,
imageCounts: 0,
containerCounts: 1,
},
}

for _, test := range tests {
listResults, err := store.MultiList(test.options)
require.NoError(t, err)
require.Len(t, listResults.Layers, test.layerCounts)
require.Len(t, listResults.Images, test.imageCounts)
require.Len(t, listResults.Containers, test.containerCounts)
}

_, err = store.Shutdown(true)
require.Nil(t, err)

store.Free()
}

0 comments on commit e5de483

Please sign in to comment.