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
This commit is contained in:
Kevin Krakauer
2018-06-06 16:13:53 -07:00
committed by Shentubot
parent 79fef54eb1
commit 206e90d057
4 changed files with 121 additions and 10 deletions
+2 -2
View File
@@ -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)
}
}
+41 -2
View File
@@ -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)
+58 -1
View File
@@ -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)
}
}
}
+20 -5
View File
@@ -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.