diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container.go | 31 | ||||
-rw-r--r-- | libpod/container_api.go | 4 | ||||
-rw-r--r-- | libpod/container_easyjson.go | 2 | ||||
-rw-r--r-- | libpod/container_inspect.go | 5 | ||||
-rw-r--r-- | libpod/container_internal.go | 30 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 28 | ||||
-rw-r--r-- | libpod/image/image.go | 18 | ||||
-rw-r--r-- | libpod/image/image_test.go | 46 | ||||
-rw-r--r-- | libpod/networking_linux.go | 22 | ||||
-rw-r--r-- | libpod/oci.go | 6 | ||||
-rw-r--r-- | libpod/runtime.go | 24 |
11 files changed, 169 insertions, 47 deletions
diff --git a/libpod/container.go b/libpod/container.go index 44992c534..026eb1c4f 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1,7 +1,9 @@ package libpod import ( + "encoding/json" "fmt" + "io/ioutil" "net" "os" "path/filepath" @@ -128,6 +130,11 @@ type Container struct { rootlessSlirpSyncR *os.File rootlessSlirpSyncW *os.File + + // A restored container should have the same IP address as before + // being checkpointed. If requestedIP is set it will be used instead + // of config.StaticIP. + requestedIP net.IP } // containerState contains the current state of the container @@ -402,6 +409,30 @@ func (c *Container) Spec() *spec.Spec { return returnSpec } +// specFromState returns the unmarshalled json config of the container. If the +// config does not exist (e.g., because the container was never started) return +// the spec from the config. +func (c *Container) specFromState() (*spec.Spec, error) { + spec := c.config.Spec + + if f, err := os.Open(c.state.ConfigPath); err == nil { + content, err := ioutil.ReadAll(f) + if err != nil { + return nil, errors.Wrapf(err, "error reading container config") + } + if err := json.Unmarshal([]byte(content), &spec); err != nil { + return nil, errors.Wrapf(err, "error unmarshalling container config") + } + } else { + // ignore when the file does not exist + if !os.IsNotExist(err) { + return nil, errors.Wrapf(err, "error opening container config") + } + } + + return spec, nil +} + // ID returns the container's ID func (c *Container) ID() string { return c.config.ID diff --git a/libpod/container_api.go b/libpod/container_api.go index 09bc46905..4eaf737b0 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -262,7 +262,7 @@ func (c *Container) Kill(signal uint) error { // Exec starts a new process inside the container // TODO allow specifying streams to attach to // TODO investigate allowing exec without attaching -func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) error { +func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string) error { var capList []string locked := false @@ -324,7 +324,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID) - execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, hostUser, sessionID) + execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID) if err != nil { return errors.Wrapf(err, "error exec %s", c.ID()) } diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go index 50741df11..f1cb09bcc 100644 --- a/libpod/container_easyjson.go +++ b/libpod/container_easyjson.go @@ -1,6 +1,6 @@ // +build seccomp ostree selinux varlink exclude_graphdriver_devicemapper -// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT package libpod diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 06a0c9f32..e2730c282 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -12,7 +12,10 @@ import ( func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data) (*inspect.ContainerInspectData, error) { config := c.config runtimeInfo := c.state - spec := c.config.Spec + spec, err := c.specFromState() + if err != nil { + return nil, err + } // Process is allowed to be nil in the spec args := []string{} diff --git a/libpod/container_internal.go b/libpod/container_internal.go index cc4c36bc9..69df33bc9 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1181,6 +1181,7 @@ func (c *Container) saveSpec(spec *spec.Spec) error { return nil } +// Warning: precreate hooks may alter 'config' in place. func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (extensionStageHooks map[string][]spec.Hook, err error) { var locale string var ok bool @@ -1209,13 +1210,13 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten } } + allHooks := make(map[string][]spec.Hook) if c.runtime.config.HooksDir == nil { if rootless.IsRootless() { return nil, nil } - allHooks := make(map[string][]spec.Hook) for _, hDir := range []string{hooks.DefaultDir, hooks.OverrideDir} { - manager, err := hooks.New(ctx, []string{hDir}, []string{"poststop"}, lang) + manager, err := hooks.New(ctx, []string{hDir}, []string{"precreate", "poststop"}, lang) if err != nil { if os.IsNotExist(err) { continue @@ -1233,19 +1234,32 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten allHooks[i] = hook } } - return allHooks, nil + } else { + manager, err := hooks.New(ctx, c.runtime.config.HooksDir, []string{"precreate", "poststop"}, lang) + if err != nil { + if os.IsNotExist(err) { + logrus.Warnf("Requested OCI hooks directory %q does not exist", c.runtime.config.HooksDir) + return nil, nil + } + return nil, err + } + + allHooks, err = manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0) + if err != nil { + return nil, err + } } - manager, err := hooks.New(ctx, c.runtime.config.HooksDir, []string{"poststop"}, lang) + hookErr, err := exec.RuntimeConfigFilter(ctx, allHooks["precreate"], config, exec.DefaultPostKillTimeout) if err != nil { - if os.IsNotExist(err) { - logrus.Warnf("Requested OCI hooks directory %q does not exist", c.runtime.config.HooksDir) - return nil, nil + logrus.Warnf("container %s: precreate hook: %v", c.ID(), err) + if hookErr != nil && hookErr != err { + logrus.Debugf("container %s: precreate hook (hook error): %v", c.ID(), hookErr) } return nil, err } - return manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0) + return allHooks, nil } // mount mounts the container's root filesystem diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 0745b7732..2f03d45ea 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -20,6 +20,7 @@ import ( cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" crioAnnotations "github.com/containers/libpod/pkg/annotations" + "github.com/containers/libpod/pkg/apparmor" "github.com/containers/libpod/pkg/criu" "github.com/containers/libpod/pkg/lookup" "github.com/containers/libpod/pkg/resolvconf" @@ -185,6 +186,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } + // Apply AppArmor checks and load the default profile if needed. + updatedProfile, err := apparmor.CheckProfileAndLoadDefault(c.config.Spec.Process.ApparmorProfile) + if err != nil { + return nil, err + } + g.SetProcessApparmorProfile(updatedProfile) + if err := c.makeBindMounts(); err != nil { return nil, err } @@ -228,10 +236,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } - if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil { - return nil, errors.Wrapf(err, "error setting up OCI Hooks") - } - // Bind builtin image volumes if c.config.Rootfs == "" && c.config.ImageVolumes { if err := c.addLocalVolumes(ctx, &g, execUser); err != nil { @@ -384,6 +388,12 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { logrus.Debugf("set root propagation to %q", rootPropagation) g.SetLinuxRootPropagation(rootPropagation) } + + // Warning: precreate hooks may alter g.Config in place. + if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil { + return nil, errors.Wrapf(err, "error setting up OCI Hooks") + } + return g.Config, nil } @@ -547,10 +557,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti } } if IP != nil { - env := fmt.Sprintf("IP=%s", IP) // Tell CNI which IP address we want. - os.Setenv("CNI_ARGS", env) - logrus.Debugf("Restoring container with %s", env) + c.requestedIP = IP } } @@ -566,12 +574,6 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti return err } - // TODO: use existing way to request static IPs, once it is merged in ocicni - // https://github.com/cri-o/ocicni/pull/23/ - - // CNI_ARGS was used to request a certain IP address. Unconditionally remove it. - os.Unsetenv("CNI_ARGS") - // Read config jsonPath := filepath.Join(c.bundlePath(), "config.json") logrus.Debugf("generate.NewFromFile at %v", jsonPath) diff --git a/libpod/image/image.go b/libpod/image/image.go index 3a6d0e305..2e12adb70 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -305,12 +305,24 @@ func (i *Image) Names() []string { } // RepoDigests returns a string array of repodigests associated with the image -func (i *Image) RepoDigests() []string { +func (i *Image) RepoDigests() ([]string, error) { var repoDigests []string + digest := i.Digest() + for _, name := range i.Names() { - repoDigests = append(repoDigests, strings.SplitN(name, ":", 2)[0]+"@"+i.Digest().String()) + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, err + } + + canonical, err := reference.WithDigest(reference.TrimNamed(named), digest) + if err != nil { + return nil, err + } + + repoDigests = append(repoDigests, canonical.String()) } - return repoDigests + return repoDigests, nil } // Created returns the time the image was created diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index 91bb2411b..2a68fd273 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/containers/storage" + "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" ) @@ -192,6 +193,51 @@ func TestImage_MatchRepoTag(t *testing.T) { cleanup(workdir, ir) } +// TestImage_RepoDigests tests RepoDigest generation. +func TestImage_RepoDigests(t *testing.T) { + dgst, err := digest.Parse("sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc") + if err != nil { + t.Fatal(err) + } + + for _, test := range []struct { + name string + names []string + expected []string + }{ + { + name: "empty", + names: []string{}, + expected: nil, + }, + { + name: "tagged", + names: []string{"docker.io/library/busybox:latest"}, + expected: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"}, + }, + { + name: "digest", + names: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"}, + expected: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"}, + }, + } { + t.Run(test.name, func(t *testing.T) { + image := &Image{ + image: &storage.Image{ + Names: test.names, + Digest: dgst, + }, + } + actual, err := image.RepoDigests() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, test.expected, actual) + }) + } +} + // Test_splitString tests the splitString function in image that // takes input and splits on / and returns the last array item func Test_splitString(t *testing.T) { diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 43d0a61a4..a343bee6a 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -50,7 +50,16 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port // Create and configure a new network namespace for a container func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) { - podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP) + var requestedIP net.IP + if ctr.requestedIP != nil { + requestedIP = ctr.requestedIP + // cancel request for a specific IP in case the container is reused later + ctr.requestedIP = nil + } else { + requestedIP = ctr.config.StaticIP + } + + podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP) results, err := r.netPlugin.SetUpPod(podNetwork) if err != nil { @@ -258,7 +267,16 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID()) - podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP) + var requestedIP net.IP + if ctr.requestedIP != nil { + requestedIP = ctr.requestedIP + // cancel request for a specific IP in case the container is reused later + ctr.requestedIP = nil + } else { + requestedIP = ctr.config.StaticIP + } + + podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP) // The network may have already been torn down, so don't fail here, just log if err := r.netPlugin.TearDownPod(podNetwork); err != nil { diff --git a/libpod/oci.go b/libpod/oci.go index 093bfdd35..31c1a7e85 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -728,7 +728,7 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error { // TODO: Add --detach support // TODO: Convert to use conmon // TODO: add --pid-file and use that to generate exec session tracking -func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, user, sessionID string) (*exec.Cmd, error) { +func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string) (*exec.Cmd, error) { if len(cmd) == 0 { return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute") } @@ -749,7 +749,9 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty args = append(args, "exec") - args = append(args, "--cwd", c.config.Spec.Process.Cwd) + if cwd != "" { + args = append(args, "--cwd", cwd) + } args = append(args, "--pid-file", c.execPidPath(sessionID)) diff --git a/libpod/runtime.go b/libpod/runtime.go index ab8d02a4f..c9471247c 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -692,25 +692,19 @@ func makeRuntime(runtime *Runtime) (err error) { } } - // Set up the lock manager - var manager lock.Manager lockPath := DefaultSHMLockPath if rootless.IsRootless() { lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) } - if doRefresh { - // If SHM locks already exist, delete them and reinitialize - if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil && !os.IsNotExist(err) { - return errors.Wrapf(err, "error deleting existing libpod SHM segment %s", lockPath) - } - - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return err - } - } else { - manager, err = lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { + // Set up the lock manager + manager, err := lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + return err + } + } else { return err } } |