diff options
author | Kevin Krakauer <krakauer@google.com> | 2018-06-06 16:12:58 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-06-06 16:13:53 -0700 |
commit | 206e90d057211f2ac53174907b2ff04801f9a481 (patch) | |
tree | 8f4ff7b6847838a3d2c8e2a872bc0b599ac11de2 /runsc | |
parent | 79fef54eb1b9e941e2c910f90b65f3cfe94e18c4 (diff) |
runsc: Support abbreviated container IDs.
Just a UI/usability addition. It's a lot easier to type "60" than
"60185c721d7e10c00489f1fa210ee0d35c594873d6376b457fb1815e4fdbfc2c".
PiperOrigin-RevId: 199547932
Change-Id: I19011b5061a88aba48a9ad7f8cf954a6782de854
Diffstat (limited to 'runsc')
-rw-r--r-- | runsc/cmd/delete_test.go | 4 | ||||
-rw-r--r-- | runsc/container/container.go | 43 | ||||
-rw-r--r-- | runsc/container/container_test.go | 59 | ||||
-rw-r--r-- | runsc/test/testutil/testutil.go | 25 |
4 files changed, 121 insertions, 10 deletions
diff --git a/runsc/cmd/delete_test.go b/runsc/cmd/delete_test.go index 928e9ee2c..f6d164394 100644 --- a/runsc/cmd/delete_test.go +++ b/runsc/cmd/delete_test.go @@ -31,11 +31,11 @@ func TestNotFound(t *testing.T) { d := Delete{} if err := d.execute(ids, conf); err == nil { - t.Error("Deleting non-existend container should have failed") + t.Error("Deleting non-existent container should have failed") } d = Delete{force: true} if err := d.execute(ids, conf); err != nil { - t.Errorf("Deleting non-existend container with --force should NOT have failed: %v", err) + t.Errorf("Deleting non-existent container with --force should NOT have failed: %v", err) } } diff --git a/runsc/container/container.go b/runsc/container/container.go index eee148f5a..66a2f27a1 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -23,6 +23,7 @@ import ( "path/filepath" "regexp" "strconv" + "strings" "syscall" "time" @@ -92,14 +93,22 @@ type Container struct { Sandbox *sandbox.Sandbox `json:"sandbox"` } -// Load loads a container with the given id from a metadata file. +// Load loads a container with the given id from a metadata file. id may be an +// abbreviation of the full container id, in which case Load loads the +// container to which id unambiguously refers to. // Returns ErrNotExist if container doesn't exits. func Load(rootDir, id string) (*Container, error) { log.Debugf("Load container %q %q", rootDir, id) if err := validateID(id); err != nil { return nil, err } - metaFile := filepath.Join(rootDir, id, metadataFilename) + + cRoot, err := findContainerRoot(rootDir, id) + if err != nil { + return nil, err + } + + metaFile := filepath.Join(cRoot, metadataFilename) metaBytes, err := ioutil.ReadFile(metaFile) if err != nil { if os.IsNotExist(err) { @@ -133,6 +142,36 @@ func Load(rootDir, id string) (*Container, error) { return &c, nil } +func findContainerRoot(rootDir, partialID string) (string, error) { + // Check whether the id fully specifies an existing container. + cRoot := filepath.Join(rootDir, partialID) + if _, err := os.Stat(cRoot); err == nil { + return cRoot, nil + } + + // Now see whether id could be an abbreviation of exactly 1 of the + // container ids. If id is ambigious (it could match more than 1 + // container), it is an error. + cRoot = "" + ids, err := List(rootDir) + if err != nil { + return "", err + } + for _, id := range ids { + if strings.HasPrefix(id, partialID) { + if cRoot != "" { + return "", fmt.Errorf("id %q is ambiguous and could refer to multiple containers: %q, %q", partialID, cRoot, id) + } + cRoot = id + } + } + if cRoot == "" { + return "", os.ErrNotExist + } + log.Debugf("abbreviated id %q resolves to full id %q", partialID, cRoot) + return filepath.Join(rootDir, cRoot), nil +} + // List returns all container ids in the given root directory. func List(rootDir string) ([]string, error) { log.Debugf("List containers %q", rootDir) diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index da59c0331..43cd177ce 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -634,7 +634,7 @@ func TestRunNonRoot(t *testing.T) { } } -// TestMountNewDir check that runsc will create destination directory if it +// TestMountNewDir checks that runsc will create destination directory if it // doesn't exit. func TestMountNewDir(t *testing.T) { srcDir := path.Join(os.TempDir(), "src", "newdir", "anotherdir") @@ -660,3 +660,60 @@ func TestMountNewDir(t *testing.T) { t.Fatalf("error running sadbox: %v", err) } } + +// TestAbbreviatedIDs checks that runsc supports using abbreviated container +// IDs in place of full IDs. +func TestAbbreviatedIDs(t *testing.T) { + cids := []string{ + "foo-" + testutil.UniqueContainerID(), + "bar-" + testutil.UniqueContainerID(), + "baz-" + testutil.UniqueContainerID(), + } + + rootDir, err := testutil.SetupRootDir() + if err != nil { + t.Fatalf("error creating root dir: %v", err) + } + for _, cid := range cids { + spec := testutil.NewSpecWithArgs("sleep", "100") + bundleDir, conf, err := testutil.SetupContainerInRoot(rootDir, spec) + if err != nil { + t.Fatalf("error setting up container: %v", err) + } + defer os.RemoveAll(rootDir) + defer os.RemoveAll(bundleDir) + + // Create and start the container. + cont, err := container.Create(cid, spec, conf, bundleDir, "", "") + if err != nil { + t.Fatalf("error creating container: %v", err) + } + defer cont.Destroy() + } + + // These should all be unambigious. + unambiguous := map[string]string{ + "f": cids[0], + cids[0]: cids[0], + "bar": cids[1], + cids[1]: cids[1], + "baz": cids[2], + cids[2]: cids[2], + } + for shortid, longid := range unambiguous { + if _, err := container.Load(rootDir, shortid); err != nil { + t.Errorf("%q should resolve to %q: %v", shortid, longid, err) + } + } + + // These should be ambiguous. + ambiguous := []string{ + "b", + "ba", + } + for _, shortid := range ambiguous { + if s, err := container.Load(rootDir, shortid); err == nil { + t.Errorf("%q should be ambiguous, but resolved to %q", shortid, s.ID) + } + } +} diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index 87db0a170..1c8fd3ba2 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -84,21 +84,36 @@ func NewSpecWithArgs(args ...string) *specs.Spec { return spec } +// SetupRootDir creates a root directory for containers. +func SetupRootDir() (string, error) { + rootDir, err := ioutil.TempDir("", "containers") + if err != nil { + return "", fmt.Errorf("error creating root dir: %v", err) + } + return rootDir, nil +} + // SetupContainer creates a bundle and root dir for the container, generates a // test config, and writes the spec to config.json in the bundle dir. func SetupContainer(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Config, err error) { - rootDir, err = ioutil.TempDir("", "containers") + rootDir, err = SetupRootDir() if err != nil { - return "", "", nil, fmt.Errorf("error creating root dir: %v", err) + return "", "", nil, err } + bundleDir, conf, err = SetupContainerInRoot(rootDir, spec) + return rootDir, bundleDir, conf, err +} +// SetupContainerInRoot creates a bundle for the container, generates a test +// config, and writes the spec to config.json in the bundle dir. +func SetupContainerInRoot(rootDir string, spec *specs.Spec) (bundleDir string, conf *boot.Config, err error) { bundleDir, err = ioutil.TempDir("", "bundle") if err != nil { - return "", "", nil, fmt.Errorf("error creating bundle dir: %v", err) + return "", nil, fmt.Errorf("error creating bundle dir: %v", err) } if err = writeSpec(bundleDir, spec); err != nil { - return "", "", nil, fmt.Errorf("error writing spec: %v", err) + return "", nil, fmt.Errorf("error writing spec: %v", err) } conf = &boot.Config{ @@ -110,7 +125,7 @@ func SetupContainer(spec *specs.Spec) (rootDir, bundleDir string, conf *boot.Con TestModeNoFlags: true, } - return rootDir, bundleDir, conf, nil + return bundleDir, conf, nil } // writeSpec writes the spec to disk in the given directory. |