summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
authorKevin Krakauer <krakauer@google.com>2019-04-23 11:32:34 -0700
committerShentubot <shentubot@google.com>2019-04-23 11:33:40 -0700
commitdf21460cfdf589299e98171407741e3c253debe4 (patch)
treecdafaa500c7be14d4c9300776061f9b8f921b3fc /runsc
parent17ff6063a37551e83eebab98616a21bbc7e58764 (diff)
Fix container_test flakes.
Create, Start, and Destroy were racing to create and destroy the metadata directory of containers. This is a re-upload of https://gvisor-review.googlesource.com/c/gvisor/+/16260, but with the correct account. Change-Id: I16b7a9d0971f0df873e7f4145e6ac8f72730a4f1 PiperOrigin-RevId: 244892991
Diffstat (limited to 'runsc')
-rw-r--r--runsc/container/container.go78
1 files changed, 67 insertions, 11 deletions
diff --git a/runsc/container/container.go b/runsc/container/container.go
index cc0c1ee25..1bed1a97e 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -99,7 +99,9 @@ type Container struct {
// BundleDir is the directory containing the container bundle.
BundleDir string `json:"bundleDir"`
- // Root is the directory containing the container metadata file.
+ // Root is the directory containing the container metadata file. If this
+ // container is the root container, Root and RootContainerDir will be the
+ // same.
Root string `json:"root"`
// CreatedAt is the time the container was created.
@@ -128,6 +130,12 @@ type Container struct {
// Sandbox is the sandbox this container is running in. It's set when the
// container is created and reset when the sandbox is destroyed.
Sandbox *sandbox.Sandbox `json:"sandbox"`
+
+ // RootContainerDir is the root directory containing the metadata file of the
+ // sandbox root container. It's used to lock in order to serialize creating
+ // and deleting this Container's metadata directory. If this container is the
+ // root container, this is the same as Root.
+ RootContainerDir string
}
// Load loads a container with the given id from a metadata file. id may be an
@@ -243,6 +251,12 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo
return nil, err
}
+ unlockRoot, err := maybeLockRootContainer(spec, conf.RootDir)
+ if err != nil {
+ return nil, err
+ }
+ defer unlockRoot()
+
// Lock the container metadata file to prevent concurrent creations of
// containers with the same id.
containerRoot := filepath.Join(conf.RootDir, id)
@@ -261,14 +275,15 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo
}
c := &Container{
- ID: id,
- Spec: spec,
- ConsoleSocket: consoleSocket,
- BundleDir: bundleDir,
- Root: containerRoot,
- Status: Creating,
- CreatedAt: time.Now(),
- Owner: os.Getenv("USER"),
+ ID: id,
+ Spec: spec,
+ ConsoleSocket: consoleSocket,
+ BundleDir: bundleDir,
+ Root: containerRoot,
+ Status: Creating,
+ CreatedAt: time.Now(),
+ Owner: os.Getenv("USER"),
+ RootContainerDir: conf.RootDir,
}
// The Cleanup object cleans up partially created containers when an error occurs.
// Any errors occuring during cleanup itself are ignored.
@@ -279,7 +294,7 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo
// started in an existing sandbox, we must do so. The metadata will
// indicate the ID of the sandbox, which is the same as the ID of the
// init container in the sandbox.
- if specutils.ShouldCreateSandbox(spec) {
+ if isRoot(spec) {
log.Debugf("Creating new sandbox for container %q", id)
// Create and join cgroup before processes are created to ensure they are
@@ -354,6 +369,13 @@ func Create(id string, spec *specs.Spec, conf *boot.Config, bundleDir, consoleSo
// Start starts running the containerized process inside the sandbox.
func (c *Container) Start(conf *boot.Config) error {
log.Debugf("Start container %q", c.ID)
+
+ unlockRoot, err := maybeLockRootContainer(c.Spec, c.RootContainerDir)
+ if err != nil {
+ return err
+ }
+ defer unlockRoot()
+
unlock, err := c.lock()
if err != nil {
return err
@@ -371,7 +393,7 @@ func (c *Container) Start(conf *boot.Config) error {
}
}
- if specutils.ShouldCreateSandbox(c.Spec) {
+ if isRoot(c.Spec) {
if err := c.Sandbox.StartRoot(c.Spec, conf); err != nil {
return err
}
@@ -418,6 +440,7 @@ func (c *Container) Restore(spec *specs.Spec, conf *boot.Config, restoreFile str
return err
}
defer unlock()
+
if err := c.requireStatus("restore", Created); err != nil {
return err
}
@@ -644,6 +667,12 @@ func (c *Container) Destroy() error {
// of errors return their concatenation.
var errs []string
+ unlock, err := maybeLockRootContainer(c.Spec, c.RootContainerDir)
+ if err != nil {
+ return err
+ }
+ defer unlock()
+
if err := c.stop(); err != nil {
err = fmt.Errorf("stopping container: %v", err)
log.Warningf("%v", err)
@@ -960,6 +989,33 @@ func lockContainerMetadata(containerRootDir string) (func() error, error) {
return l.Unlock, nil
}
+// maybeLockRootContainer locks the sandbox root container. It is used to
+// prevent races to create and delete child container sandboxes.
+func maybeLockRootContainer(spec *specs.Spec, rootDir string) (func() error, error) {
+ if isRoot(spec) {
+ return func() error { return nil }, nil
+ }
+
+ sbid, ok := specutils.SandboxID(spec)
+ if !ok {
+ return nil, fmt.Errorf("no sandbox ID found when locking root container")
+ }
+ sb, err := Load(rootDir, sbid)
+ if err != nil {
+ return nil, err
+ }
+
+ unlock, err := sb.lock()
+ if err != nil {
+ return nil, err
+ }
+ return unlock, nil
+}
+
+func isRoot(spec *specs.Spec) bool {
+ return specutils.ShouldCreateSandbox(spec)
+}
+
// runInCgroup executes fn inside the specified cgroup. If cg is nil, execute
// it in the current context.
func runInCgroup(cg *cgroup.Cgroup, fn func() error) error {