summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
authorKevin Krakauer <krakauer@google.com>2018-06-06 16:12:58 -0700
committerShentubot <shentubot@google.com>2018-06-06 16:13:53 -0700
commit206e90d057211f2ac53174907b2ff04801f9a481 (patch)
tree8f4ff7b6847838a3d2c8e2a872bc0b599ac11de2 /runsc
parent79fef54eb1b9e941e2c910f90b65f3cfe94e18c4 (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.go4
-rw-r--r--runsc/container/container.go43
-rw-r--r--runsc/container/container_test.go59
-rw-r--r--runsc/test/testutil/testutil.go25
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.