mirror of https://github.com/containers/image.git
Expand store location to include runRoot and opts
Expand the syntax of the store's location to allow it to also contain the runRoot location (optional, after the root, separated by '+') and a list of driver options (optional, after the runRoot, separated by ":", and itself broken up using ","). Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
98012c4cec
commit
2b1346c03c
|
|
@ -83,7 +83,12 @@ func (s storageReference) DockerReference() reference.Named {
|
|||
// disambiguate between images which may be present in multiple stores and
|
||||
// share only their names.
|
||||
func (s storageReference) StringWithinTransport() string {
|
||||
storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "]"
|
||||
optionsList := ""
|
||||
options := s.transport.store.GraphOptions()
|
||||
if len(options) > 0 {
|
||||
optionsList = ":" + strings.Join(options, ",")
|
||||
}
|
||||
storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "+" + s.transport.store.RunRoot() + optionsList + "]"
|
||||
if s.name == nil {
|
||||
return storeSpec + "@" + s.id
|
||||
}
|
||||
|
|
@ -94,7 +99,14 @@ func (s storageReference) StringWithinTransport() string {
|
|||
}
|
||||
|
||||
func (s storageReference) PolicyConfigurationIdentity() string {
|
||||
return s.StringWithinTransport()
|
||||
storeSpec := "[" + s.transport.store.GraphDriverName() + "@" + s.transport.store.GraphRoot() + "]"
|
||||
if s.name == nil {
|
||||
return storeSpec + "@" + s.id
|
||||
}
|
||||
if s.id == "" {
|
||||
return storeSpec + s.reference
|
||||
}
|
||||
return storeSpec + s.reference + "@" + s.id
|
||||
}
|
||||
|
||||
// Also accept policy that's tied to the combination of the graph root and
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package storage
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -57,7 +58,12 @@ var validReferenceTestCases = []struct {
|
|||
|
||||
func TestStorageReferenceStringWithinTransport(t *testing.T) {
|
||||
store := newStore(t)
|
||||
storeSpec := fmt.Sprintf("[%s@%s]", store.GraphDriverName(), store.GraphRoot())
|
||||
optionsList := ""
|
||||
options := store.GraphOptions()
|
||||
if len(options) > 0 {
|
||||
optionsList = ":" + strings.Join(options, ",")
|
||||
}
|
||||
storeSpec := fmt.Sprintf("[%s@%s+%s%s]", store.GraphDriverName(), store.GraphRoot(), store.RunRoot(), optionsList)
|
||||
|
||||
for _, c := range validReferenceTestCases {
|
||||
ref, err := Transport.ParseReference(c.input)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ func TestMain(m *testing.M) {
|
|||
os.Exit(code)
|
||||
}
|
||||
|
||||
func newStore(t *testing.T) storage.Store {
|
||||
func newStoreWithGraphDriverOptions(t *testing.T, options []string) storage.Store {
|
||||
wd, err := ioutil.TempDir(topwd, "test.")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -83,7 +83,7 @@ func newStore(t *testing.T) storage.Store {
|
|||
RunRoot: run,
|
||||
GraphRoot: root,
|
||||
GraphDriverName: "vfs",
|
||||
GraphDriverOptions: []string{},
|
||||
GraphDriverOptions: options,
|
||||
UIDMap: uidmap,
|
||||
GIDMap: gidmap,
|
||||
})
|
||||
|
|
@ -94,6 +94,10 @@ func newStore(t *testing.T) storage.Store {
|
|||
return store
|
||||
}
|
||||
|
||||
func newStore(t *testing.T) storage.Store {
|
||||
return newStoreWithGraphDriverOptions(t, []string{})
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
store := newStore(t)
|
||||
|
||||
|
|
@ -162,6 +166,46 @@ func TestParse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParseWithGraphDriverOptions(t *testing.T) {
|
||||
optionLists := [][]string{
|
||||
{},
|
||||
{"unused1"},
|
||||
{"unused1", "unused2"},
|
||||
{"unused1", "unused2", "unused3"},
|
||||
}
|
||||
for _, optionList := range optionLists {
|
||||
store := newStoreWithGraphDriverOptions(t, optionList)
|
||||
ref, err := Transport.ParseStoreReference(store, "test")
|
||||
if err != nil {
|
||||
t.Fatalf("ParseStoreReference(%q, graph driver options %v) returned error %v", "test", optionList, err)
|
||||
}
|
||||
if ref == nil {
|
||||
t.Fatalf("ParseStoreReference returned nil reference")
|
||||
}
|
||||
spec := ref.StringWithinTransport()
|
||||
ref2, err := Transport.ParseReference(spec)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseReference(%q) returned error %v", "test", err)
|
||||
}
|
||||
if ref == nil {
|
||||
t.Fatalf("ParseReference returned nil reference")
|
||||
}
|
||||
sref, ok := ref2.(*storageReference)
|
||||
if !ok {
|
||||
t.Fatalf("ParseReference returned a reference from transport %s, not one of ours", ref2.Transport().Name())
|
||||
}
|
||||
parsedOptions := sref.transport.store.GraphOptions()
|
||||
if len(parsedOptions) != len(optionList) {
|
||||
t.Fatalf("Lost options between %v and %v", optionList, parsedOptions)
|
||||
}
|
||||
for i := range optionList {
|
||||
if parsedOptions[i] != optionList[i] {
|
||||
t.Fatalf("Mismatched option %d: %v and %v", i, optionList[i], parsedOptions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func systemContext() *types.SystemContext {
|
||||
return &types.SystemContext{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,12 @@ func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (
|
|||
// recognize.
|
||||
return nil, ErrInvalidReference
|
||||
}
|
||||
storeSpec := "[" + store.GraphDriverName() + "@" + store.GraphRoot() + "]"
|
||||
optionsList := ""
|
||||
options := store.GraphOptions()
|
||||
if len(options) > 0 {
|
||||
optionsList = ":" + strings.Join(options, ",")
|
||||
}
|
||||
storeSpec := "[" + store.GraphDriverName() + "@" + store.GraphRoot() + "+" + store.RunRoot() + optionsList + "]"
|
||||
id := ""
|
||||
if sum.Validate() == nil {
|
||||
id = sum.Hex()
|
||||
|
|
@ -145,15 +150,11 @@ func (s *storageTransport) GetStore() (storage.Store, error) {
|
|||
|
||||
// ParseReference takes a name and/or an ID ("_name_"/"@_id_"/"_name_@_id_"),
|
||||
// possibly prefixed with a store specifier in the form "[_graphroot_]" or
|
||||
// "[_driver_@_graphroot_]", tries to figure out which it is, and returns it in
|
||||
// a reference object. If the _graphroot_ is a location other than the default,
|
||||
// it needs to have been previously opened using storage.GetStore(), so that it
|
||||
// can figure out which run root goes with the graph root.
|
||||
// "[_driver_@_graphroot_]" or "[_driver_@_graphroot_+_runroot_]" or
|
||||
// "[_driver_@_graphroot_:_options_]" or "[_driver_@_graphroot_+_runroot_:_options_]",
|
||||
// tries to figure out which it is, and returns it in a reference object.
|
||||
func (s *storageTransport) ParseReference(reference string) (types.ImageReference, error) {
|
||||
store, err := s.GetStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var store storage.Store
|
||||
// Check if there's a store location prefix. If there is, then it
|
||||
// needs to match a store that was previously initialized using
|
||||
// storage.GetStore(), or be enough to let the storage library fill out
|
||||
|
|
@ -165,37 +166,63 @@ func (s *storageTransport) ParseReference(reference string) (types.ImageReferenc
|
|||
}
|
||||
storeSpec := reference[1:closeIndex]
|
||||
reference = reference[closeIndex+1:]
|
||||
storeInfo := strings.SplitN(storeSpec, "@", 2)
|
||||
if len(storeInfo) == 1 && storeInfo[0] != "" {
|
||||
// One component: the graph root.
|
||||
if !filepath.IsAbs(storeInfo[0]) {
|
||||
// Peel off a "driver@" from the start.
|
||||
driverInfo := ""
|
||||
driverSplit := strings.SplitN(storeSpec, "@", 2)
|
||||
if len(driverSplit) != 2 {
|
||||
if storeSpec == "" {
|
||||
return nil, ErrInvalidReference
|
||||
}
|
||||
} else {
|
||||
driverInfo = driverSplit[0]
|
||||
if driverInfo == "" {
|
||||
return nil, ErrInvalidReference
|
||||
}
|
||||
storeSpec = driverSplit[1]
|
||||
if storeSpec == "" {
|
||||
return nil, ErrInvalidReference
|
||||
}
|
||||
}
|
||||
// Peel off a ":options" from the end.
|
||||
var options []string
|
||||
optionsSplit := strings.SplitN(storeSpec, ":", 2)
|
||||
if len(optionsSplit) == 2 {
|
||||
options = strings.Split(optionsSplit[1], ",")
|
||||
storeSpec = optionsSplit[0]
|
||||
}
|
||||
// Peel off a "+runroot" from the new end.
|
||||
runRootInfo := ""
|
||||
runRootSplit := strings.SplitN(storeSpec, "+", 2)
|
||||
if len(runRootSplit) == 2 {
|
||||
runRootInfo = runRootSplit[1]
|
||||
storeSpec = runRootSplit[0]
|
||||
}
|
||||
// The rest is our graph root.
|
||||
rootInfo := storeSpec
|
||||
// Check that any paths are absolute paths.
|
||||
if rootInfo != "" && !filepath.IsAbs(rootInfo) {
|
||||
return nil, ErrPathNotAbsolute
|
||||
}
|
||||
if runRootInfo != "" && !filepath.IsAbs(runRootInfo) {
|
||||
return nil, ErrPathNotAbsolute
|
||||
}
|
||||
store2, err := storage.GetStore(storage.StoreOptions{
|
||||
GraphRoot: storeInfo[0],
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store = store2
|
||||
} else if len(storeInfo) == 2 && storeInfo[0] != "" && storeInfo[1] != "" {
|
||||
// Two components: the driver type and the graph root.
|
||||
if !filepath.IsAbs(storeInfo[1]) {
|
||||
return nil, ErrPathNotAbsolute
|
||||
}
|
||||
store2, err := storage.GetStore(storage.StoreOptions{
|
||||
GraphDriverName: storeInfo[0],
|
||||
GraphRoot: storeInfo[1],
|
||||
GraphDriverName: driverInfo,
|
||||
GraphRoot: rootInfo,
|
||||
RunRoot: runRootInfo,
|
||||
GraphDriverOptions: options,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store = store2
|
||||
} else {
|
||||
// Anything else: store specified in a form we don't
|
||||
// recognize.
|
||||
return nil, ErrInvalidReference
|
||||
// We didn't have a store spec, so use the default.
|
||||
store2, err := s.GetStore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
store = store2
|
||||
}
|
||||
return s.ParseStoreReference(store, reference)
|
||||
}
|
||||
|
|
@ -250,7 +277,7 @@ func (s storageTransport) ValidatePolicyConfigurationScope(scope string) error {
|
|||
return ErrPathNotAbsolute
|
||||
}
|
||||
} else {
|
||||
// Anything else: store specified in a form we don't
|
||||
// Anything else: scope specified in a form we don't
|
||||
// recognize.
|
||||
return ErrInvalidReference
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,24 +68,24 @@ func TestTransportParseReference(t *testing.T) {
|
|||
driver := store.GraphDriverName()
|
||||
root := store.GraphRoot()
|
||||
|
||||
for _, c := range []struct{ prefix, expectedDriver, expectedRoot string }{
|
||||
{"", driver, root}, // Implicit store location prefix
|
||||
{"[unterminated", "", ""}, // Unterminated store specifier
|
||||
{"[]", "", ""}, // Empty store specifier
|
||||
{"[relative/path]", "", ""}, // Non-absolute graph root path
|
||||
{"[" + driver + "@relative/path]", "", ""}, // Non-absolute graph root path
|
||||
{"[thisisunknown@" + root + "suffix2]", "", ""}, // Unknown graph driver
|
||||
|
||||
// The next two could be valid, but aren't enough to allow GetStore() to locate a matching
|
||||
// store, since the reference can't specify a RunRoot. Without one, GetStore() tries to
|
||||
// match the GraphRoot (possibly combined with the driver name) against a Store that was
|
||||
// previously opened using GetStore(), and we haven't done that.
|
||||
// Future versions of the storage library will probably make this easier for locations that
|
||||
// are shared, by caching the rest of the information inside the graph root so that it can
|
||||
// be looked up later, but since this is a per-test temporary location, that won't help here.
|
||||
//{"[" + root + "suffix1]", driver, root + "suffix1"}, // A valid root path
|
||||
//{"[" + driver + "@" + root + "suffix3]", driver, root + "suffix3"}, // A valid root@graph pair
|
||||
for _, c := range []struct{ prefix, expectedDriver, expectedRoot, expectedRunRoot string }{
|
||||
{"", driver, root, ""}, // Implicit store location prefix
|
||||
{"[unterminated", "", "", ""}, // Unterminated store specifier
|
||||
{"[]", "", "", ""}, // Empty store specifier
|
||||
{"[relative/path]", "", "", ""}, // Non-absolute graph root path
|
||||
{"[" + driver + "@relative/path]", "", "", ""}, // Non-absolute graph root path
|
||||
{"[thisisunknown@" + root + "suffix2]", "", "", ""}, // Unknown graph driver
|
||||
{"[" + root + "suffix1]", "", root + "suffix1", ""}, // A valid root path, but no run dir
|
||||
{"[" + driver + "@" + root + "suffix3+" + root + "suffix4]",
|
||||
driver,
|
||||
root + "suffix3",
|
||||
root + "suffix4"}, // A valid root@graph+run set
|
||||
{"[" + driver + "@" + root + "suffix3+" + root + "suffix4:options,options,options]",
|
||||
driver,
|
||||
root + "suffix3",
|
||||
root + "suffix4"}, // A valid root@graph+run+options set
|
||||
} {
|
||||
t.Logf("parsing %q", c.prefix+"busybox")
|
||||
ref, err := Transport.ParseReference(c.prefix + "busybox")
|
||||
if c.expectedDriver == "" {
|
||||
assert.Error(t, err, c.prefix)
|
||||
|
|
@ -95,6 +95,9 @@ func TestTransportParseReference(t *testing.T) {
|
|||
require.True(t, ok, c.prefix)
|
||||
assert.Equal(t, c.expectedDriver, storageRef.transport.store.GraphDriverName(), c.prefix)
|
||||
assert.Equal(t, c.expectedRoot, storageRef.transport.store.GraphRoot(), c.prefix)
|
||||
if c.expectedRunRoot != "" {
|
||||
assert.Equal(t, c.expectedRunRoot, storageRef.transport.store.RunRoot(), c.prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue