update vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
19a32db84d
commit
94d1cfbfbf
10501 changed files with 2307943 additions and 29279 deletions
1
vendor/github.com/opencontainers/runc/.gitignore
generated
vendored
1
vendor/github.com/opencontainers/runc/.gitignore
generated
vendored
|
@ -1,5 +1,6 @@
|
|||
vendor/pkg
|
||||
/runc
|
||||
/runc-*
|
||||
contrib/cmd/recvtty/recvtty
|
||||
man/man8
|
||||
release
|
||||
|
|
2
vendor/github.com/opencontainers/runc/.travis.yml
generated
vendored
2
vendor/github.com/opencontainers/runc/.travis.yml
generated
vendored
|
@ -32,4 +32,4 @@ before_install:
|
|||
script:
|
||||
- git-validation -run DCO,short-subject -v
|
||||
- make BUILDTAGS="${BUILDTAGS}"
|
||||
- make BUILDTAGS="${BUILDTAGS}" clean ci
|
||||
- make BUILDTAGS="${BUILDTAGS}" clean ci cross
|
||||
|
|
14
vendor/github.com/opencontainers/runc/Dockerfile
generated
vendored
14
vendor/github.com/opencontainers/runc/Dockerfile
generated
vendored
|
@ -1,6 +1,10 @@
|
|||
FROM golang:1.10-stretch
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
RUN dpkg --add-architecture armel \
|
||||
&& dpkg --add-architecture armhf \
|
||||
&& dpkg --add-architecture arm64 \
|
||||
&& dpkg --add-architecture ppc64el \
|
||||
&& apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
curl \
|
||||
sudo \
|
||||
|
@ -21,6 +25,8 @@ RUN apt-get update && apt-get install -y \
|
|||
python-minimal \
|
||||
uidmap \
|
||||
kmod \
|
||||
crossbuild-essential-armel crossbuild-essential-armhf crossbuild-essential-arm64 crossbuild-essential-ppc64el \
|
||||
libseccomp-dev:armel libseccomp-dev:armhf libseccomp-dev:arm64 libseccomp-dev:ppc64el \
|
||||
--no-install-recommends \
|
||||
&& apt-get clean
|
||||
|
||||
|
@ -39,10 +45,14 @@ RUN cd /tmp \
|
|||
&& rm -rf /tmp/bats
|
||||
|
||||
# install criu
|
||||
ENV CRIU_VERSION v3.7
|
||||
# For criu v3.10, a patch is needed for `@test "checkpoint --lazy-pages and restore"`.
|
||||
# Starting with v3.11, the patch will no longer be needed.
|
||||
# See https://github.com/opencontainers/runc/issues/1863#issuecomment-412074413
|
||||
ENV CRIU_VERSION v3.10
|
||||
RUN mkdir -p /usr/src/criu \
|
||||
&& curl -sSL https://github.com/checkpoint-restore/criu/archive/${CRIU_VERSION}.tar.gz | tar -v -C /usr/src/criu/ -xz --strip-components=1 \
|
||||
&& cd /usr/src/criu \
|
||||
&& curl https://github.com/checkpoint-restore/criu/commit/27034e7c64b00a1f2467afb5ebb1d5b9b1a06ce1.patch | patch -p1 \
|
||||
&& make install-criu \
|
||||
&& rm -rf /usr/src/criu
|
||||
|
||||
|
|
32
vendor/github.com/opencontainers/runc/Makefile
generated
vendored
32
vendor/github.com/opencontainers/runc/Makefile
generated
vendored
|
@ -1,6 +1,7 @@
|
|||
.PHONY: all shell dbuild man release \
|
||||
localtest localunittest localintegration \
|
||||
test unittest integration
|
||||
test unittest integration \
|
||||
cross localcross
|
||||
|
||||
GO := go
|
||||
|
||||
|
@ -39,14 +40,14 @@ contrib/cmd/recvtty/recvtty: $(SOURCES)
|
|||
$(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
|
||||
|
||||
static: $(SOURCES)
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o runc .
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo osusergo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o runc .
|
||||
CGO_ENABLED=1 $(GO) build $(EXTRA_FLAGS) -tags "$(BUILDTAGS) netgo osusergo cgo static_build" -installsuffix netgo -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
|
||||
|
||||
release:
|
||||
script/release.sh -r release/$(VERSION) -v $(VERSION)
|
||||
|
||||
dbuild: runcimage
|
||||
docker run --rm -v $(CURDIR):/go/src/$(PROJECT) --privileged $(RUNC_IMAGE) make clean all
|
||||
docker run ${DOCKER_RUN_PROXY} --rm -v $(CURDIR):/go/src/$(PROJECT) --privileged $(RUNC_IMAGE) make clean all
|
||||
|
||||
lint:
|
||||
$(GO) vet $(allpackages)
|
||||
|
@ -56,7 +57,7 @@ man:
|
|||
man/md2man-all.sh
|
||||
|
||||
runcimage:
|
||||
docker build -t $(RUNC_IMAGE) .
|
||||
docker build ${DOCKER_BUILD_PROXY} -t $(RUNC_IMAGE) .
|
||||
|
||||
test:
|
||||
make unittest integration rootlessintegration
|
||||
|
@ -65,25 +66,25 @@ localtest:
|
|||
make localunittest localintegration localrootlessintegration
|
||||
|
||||
unittest: runcimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localunittest
|
||||
docker run ${DOCKER_RUN_PROXY} -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localunittest TESTFLAGS=${TESTFLAGS}
|
||||
|
||||
localunittest: all
|
||||
$(GO) test -timeout 3m -tags "$(BUILDTAGS)" ${TESTFLAGS} -v $(allpackages)
|
||||
|
||||
integration: runcimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localintegration
|
||||
docker run ${DOCKER_RUN_PROXY} -t --privileged --rm -v /lib/modules:/lib/modules:ro -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localintegration TESTPATH=${TESTPATH}
|
||||
|
||||
localintegration: all
|
||||
bats -t tests/integration${TESTFLAGS}
|
||||
bats -t tests/integration${TESTPATH}
|
||||
|
||||
rootlessintegration: runcimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localrootlessintegration
|
||||
docker run ${DOCKER_RUN_PROXY} -t --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localrootlessintegration
|
||||
|
||||
localrootlessintegration: all
|
||||
tests/rootless.sh
|
||||
|
||||
shell: runcimage
|
||||
docker run -e TESTFLAGS -ti --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) bash
|
||||
docker run ${DOCKER_RUN_PROXY} -ti --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) bash
|
||||
|
||||
install:
|
||||
install -D -m0755 runc $(BINDIR)/runc
|
||||
|
@ -105,7 +106,7 @@ uninstall-man:
|
|||
rm -f $(addprefix $(MAN_INSTALL_PATH),$(MAN_PAGES_BASE))
|
||||
|
||||
clean:
|
||||
rm -f runc
|
||||
rm -f runc runc-*
|
||||
rm -f contrib/cmd/recvtty/recvtty
|
||||
rm -rf $(RELEASE_DIR)
|
||||
rm -rf $(MAN_DIR)
|
||||
|
@ -117,6 +118,15 @@ validate:
|
|||
|
||||
ci: validate test release
|
||||
|
||||
cross: runcimage
|
||||
docker run ${DOCKER_RUN_PROXY} -e BUILDTAGS="$(BUILDTAGS)" --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_IMAGE) make localcross
|
||||
|
||||
localcross:
|
||||
CGO_ENABLED=1 GOARCH=arm GOARM=6 CC=arm-linux-gnueabi-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-armel .
|
||||
CGO_ENABLED=1 GOARCH=arm GOARM=7 CC=arm-linux-gnueabihf-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-armhf .
|
||||
CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-arm64 .
|
||||
CGO_ENABLED=1 GOARCH=ppc64le CC=powerpc64le-linux-gnu-gcc $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc-ppc64le .
|
||||
|
||||
# memoize allpackages, so that it's executed only once and only if used
|
||||
_allpackages = $(shell $(GO) list ./... | grep -v vendor)
|
||||
allpackages = $(if $(__allpackages),,$(eval __allpackages := $$(_allpackages)))$(__allpackages)
|
||||
|
|
12
vendor/github.com/opencontainers/runc/README.md
generated
vendored
12
vendor/github.com/opencontainers/runc/README.md
generated
vendored
|
@ -87,6 +87,18 @@ You can run a specific test case by setting the `TESTFLAGS` variable.
|
|||
# make test TESTFLAGS="-run=SomeTestFunction"
|
||||
```
|
||||
|
||||
You can run a specific integration test by setting the `TESTPATH` variable.
|
||||
|
||||
```bash
|
||||
# make test TESTPATH="/checkpoint.bats"
|
||||
```
|
||||
|
||||
You can run a test in your proxy environment by setting `DOCKER_BUILD_PROXY` and `DOCKER_RUN_PROXY` variables.
|
||||
|
||||
```bash
|
||||
# make test DOCKER_BUILD_PROXY="--build-arg HTTP_PROXY=http://yourproxy/" DOCKER_RUN_PROXY="-e HTTP_PROXY=http://yourproxy/"
|
||||
```
|
||||
|
||||
### Dependencies Management
|
||||
|
||||
`runc` uses [vndr](https://github.com/LK4D4/vndr) for dependencies management.
|
||||
|
|
9
vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
9
vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
|
@ -44,7 +44,11 @@ checkpointed.`,
|
|||
return err
|
||||
}
|
||||
// XXX: Currently this is untested with rootless containers.
|
||||
if isRootless() {
|
||||
rootless, err := isRootless(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rootless {
|
||||
return fmt.Errorf("runc checkpoint requires root")
|
||||
}
|
||||
|
||||
|
@ -121,7 +125,8 @@ var namespaceMapping = map[specs.LinuxNamespaceType]int{
|
|||
}
|
||||
|
||||
func setEmptyNsMask(context *cli.Context, options *libcontainer.CriuOpts) error {
|
||||
var nsmask int
|
||||
/* Runc doesn't manage network devices and their configuration */
|
||||
nsmask := unix.CLONE_NEWNET
|
||||
|
||||
for _, ns := range context.StringSlice("empty-ns") {
|
||||
f, exists := namespaceMapping[specs.LinuxNamespaceType(ns)]
|
||||
|
|
238
vendor/github.com/opencontainers/runc/contrib/cmd/recvtty/recvtty.go
generated
vendored
Normal file
238
vendor/github.com/opencontainers/runc/contrib/cmd/recvtty/recvtty.go
generated
vendored
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright 2016 SUSE LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// version will be populated by the Makefile, read from
|
||||
// VERSION file of the source code.
|
||||
var version = ""
|
||||
|
||||
// gitCommit will be the hash that the binary was built from
|
||||
// and will be populated by the Makefile
|
||||
var gitCommit = ""
|
||||
|
||||
const (
|
||||
usage = `Open Container Initiative contrib/cmd/recvtty
|
||||
|
||||
recvtty is a reference implementation of a consumer of runC's --console-socket
|
||||
API. It has two main modes of operation:
|
||||
|
||||
* single: Only permit one terminal to be sent to the socket, which is
|
||||
then hooked up to the stdio of the recvtty process. This is useful
|
||||
for rudimentary shell management of a container.
|
||||
|
||||
* null: Permit as many terminals to be sent to the socket, but they
|
||||
are read to /dev/null. This is used for testing, and imitates the
|
||||
old runC API's --console=/dev/pts/ptmx hack which would allow for a
|
||||
similar trick. This is probably not what you want to use, unless
|
||||
you're doing something like our bats integration tests.
|
||||
|
||||
To use recvtty, just specify a socket path at which you want to receive
|
||||
terminals:
|
||||
|
||||
$ recvtty [--mode <single|null>] socket.sock
|
||||
`
|
||||
)
|
||||
|
||||
func bail(err error) {
|
||||
fmt.Fprintf(os.Stderr, "[recvtty] fatal error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func handleSingle(path string) error {
|
||||
// Open a socket.
|
||||
ln, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
// We only accept a single connection, since we can only really have
|
||||
// one reader for os.Stdin. Plus this is all a PoC.
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Close ln, to allow for other instances to take over.
|
||||
ln.Close()
|
||||
|
||||
// Get the fd of the connection.
|
||||
unixconn, ok := conn.(*net.UnixConn)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to cast to unixconn")
|
||||
}
|
||||
|
||||
socket, err := unixconn.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer socket.Close()
|
||||
|
||||
// Get the master file descriptor from runC.
|
||||
master, err := utils.RecvFd(socket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := console.ConsoleFromFile(master)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
console.ClearONLCR(c.Fd())
|
||||
|
||||
// Copy from our stdio to the master fd.
|
||||
quitChan := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(os.Stdout, c)
|
||||
quitChan <- struct{}{}
|
||||
}()
|
||||
go func() {
|
||||
io.Copy(c, os.Stdin)
|
||||
quitChan <- struct{}{}
|
||||
}()
|
||||
|
||||
// Only close the master fd once we've stopped copying.
|
||||
<-quitChan
|
||||
c.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleNull(path string) error {
|
||||
// Open a socket.
|
||||
ln, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ln.Close()
|
||||
|
||||
// As opposed to handleSingle we accept as many connections as we get, but
|
||||
// we don't interact with Stdin at all (and we copy stdout to /dev/null).
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func(conn net.Conn) {
|
||||
// Don't leave references lying around.
|
||||
defer conn.Close()
|
||||
|
||||
// Get the fd of the connection.
|
||||
unixconn, ok := conn.(*net.UnixConn)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
socket, err := unixconn.File()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer socket.Close()
|
||||
|
||||
// Get the master file descriptor from runC.
|
||||
master, err := utils.RecvFd(socket)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Just do a dumb copy to /dev/null.
|
||||
devnull, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
// TODO: Handle this nicely.
|
||||
return
|
||||
}
|
||||
|
||||
io.Copy(devnull, master)
|
||||
devnull.Close()
|
||||
}(conn)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "recvtty"
|
||||
app.Usage = usage
|
||||
|
||||
// Set version to be the same as runC.
|
||||
var v []string
|
||||
if version != "" {
|
||||
v = append(v, version)
|
||||
}
|
||||
if gitCommit != "" {
|
||||
v = append(v, fmt.Sprintf("commit: %s", gitCommit))
|
||||
}
|
||||
app.Version = strings.Join(v, "\n")
|
||||
|
||||
// Set the flags.
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "mode, m",
|
||||
Value: "single",
|
||||
Usage: "Mode of operation (single or null)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pid-file",
|
||||
Value: "",
|
||||
Usage: "Path to write daemon process ID to",
|
||||
},
|
||||
}
|
||||
|
||||
app.Action = func(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("need to specify a single socket path")
|
||||
}
|
||||
path := ctx.Args()[0]
|
||||
|
||||
pidPath := ctx.String("pid-file")
|
||||
if pidPath != "" {
|
||||
pid := fmt.Sprintf("%d\n", os.Getpid())
|
||||
if err := ioutil.WriteFile(pidPath, []byte(pid), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch ctx.String("mode") {
|
||||
case "single":
|
||||
if err := handleSingle(path); err != nil {
|
||||
return err
|
||||
}
|
||||
case "null":
|
||||
if err := handleNull(path); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("need to select a valid mode: %s", ctx.String("mode"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
bail(err)
|
||||
}
|
||||
}
|
805
vendor/github.com/opencontainers/runc/contrib/completions/bash/runc
generated
vendored
Normal file
805
vendor/github.com/opencontainers/runc/contrib/completions/bash/runc
generated
vendored
Normal file
|
@ -0,0 +1,805 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# bash completion file for runc command
|
||||
#
|
||||
# This script provides completion of:
|
||||
# - commands and their options
|
||||
# - filepaths
|
||||
#
|
||||
# To enable the completions either:
|
||||
# - place this file in /usr/share/bash-completion/completions
|
||||
# or
|
||||
# - copy this file to e.g. ~/.runc-completion.sh and add the line
|
||||
# below to your .bashrc after bash completion features are loaded
|
||||
# . ~/.runc-completion.sh
|
||||
#
|
||||
# Configuration:
|
||||
#
|
||||
|
||||
# Note for developers:
|
||||
# Please arrange options sorted alphabetically by long name with the short
|
||||
# options immediately following their corresponding long form.
|
||||
# This order should be applied to lists, alternatives and code blocks.
|
||||
|
||||
__runc_previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
|
||||
__runc_list_all() {
|
||||
COMPREPLY=($(compgen -W "$(runc list -q)" -- $cur))
|
||||
}
|
||||
|
||||
__runc_pos_first_nonflag() {
|
||||
local argument_flags=$1
|
||||
|
||||
local counter=$((${subcommand_pos:-${command_pos}} + 1))
|
||||
while [ $counter -le $cword ]; do
|
||||
if [ -n "$argument_flags" ] && eval "case '${words[$counter]}' in $argument_flags) true ;; *) false ;; esac"; then
|
||||
((counter++))
|
||||
else
|
||||
case "${words[$counter]}" in
|
||||
-*) ;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
((counter++))
|
||||
done
|
||||
|
||||
echo $counter
|
||||
}
|
||||
|
||||
# Transforms a multiline list of strings into a single line string
|
||||
# with the words separated by "|".
|
||||
# This is used to prepare arguments to __runc_pos_first_nonflag().
|
||||
__runc_to_alternatives() {
|
||||
local parts=($1)
|
||||
local IFS='|'
|
||||
echo "${parts[*]}"
|
||||
}
|
||||
|
||||
# Transforms a multiline list of options into an extglob pattern
|
||||
# suitable for use in case statements.
|
||||
__runc_to_extglob() {
|
||||
local extglob=$(__runc_to_alternatives "$1")
|
||||
echo "@($extglob)"
|
||||
}
|
||||
|
||||
# Subcommand processing.
|
||||
# Locates the first occurrence of any of the subcommands contained in the
|
||||
# first argument. In case of a match, calls the corresponding completion
|
||||
# function and returns 0.
|
||||
# If no match is found, 1 is returned. The calling function can then
|
||||
# continue processing its completion.
|
||||
#
|
||||
# TODO if the preceding command has options that accept arguments and an
|
||||
# argument is equal to one of the subcommands, this is falsely detected as
|
||||
# a match.
|
||||
__runc_subcommands() {
|
||||
local subcommands="$1"
|
||||
|
||||
local counter=$(($command_pos + 1))
|
||||
while [ $counter -lt $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
$(__runc_to_extglob "$subcommands"))
|
||||
subcommand_pos=$counter
|
||||
local subcommand=${words[$counter]}
|
||||
local completions_func=_runc_${command}_${subcommand}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
((counter++))
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# List all Signals
|
||||
__runc_list_signals() {
|
||||
COMPREPLY=($(compgen -W "$(for i in $(kill -l | xargs); do echo $i; done | grep SIG)"))
|
||||
}
|
||||
|
||||
# suppress trailing whitespace
|
||||
__runc_nospace() {
|
||||
# compopt is not available in ancient bash versions
|
||||
type compopt &>/dev/null && compopt -o nospace
|
||||
}
|
||||
|
||||
# The list of capabilities is defined in types.go, ALL was added manually.
|
||||
__runc_complete_capabilities() {
|
||||
COMPREPLY=($(compgen -W "
|
||||
ALL
|
||||
AUDIT_CONTROL
|
||||
AUDIT_WRITE
|
||||
AUDIT_READ
|
||||
BLOCK_SUSPEND
|
||||
CHOWN
|
||||
DAC_OVERRIDE
|
||||
DAC_READ_SEARCH
|
||||
FOWNER
|
||||
FSETID
|
||||
IPC_LOCK
|
||||
IPC_OWNER
|
||||
KILL
|
||||
LEASE
|
||||
LINUX_IMMUTABLE
|
||||
MAC_ADMIN
|
||||
MAC_OVERRIDE
|
||||
MKNOD
|
||||
NET_ADMIN
|
||||
NET_BIND_SERVICE
|
||||
NET_BROADCAST
|
||||
NET_RAW
|
||||
SETFCAP
|
||||
SETGID
|
||||
SETPCAP
|
||||
SETUID
|
||||
SYS_ADMIN
|
||||
SYS_BOOT
|
||||
SYS_CHROOT
|
||||
SYSLOG
|
||||
SYS_MODULE
|
||||
SYS_NICE
|
||||
SYS_PACCT
|
||||
SYS_PTRACE
|
||||
SYS_RAWIO
|
||||
SYS_RESOURCE
|
||||
SYS_TIME
|
||||
SYS_TTY_CONFIG
|
||||
WAKE_ALARM
|
||||
" -- "$cur"))
|
||||
}
|
||||
|
||||
_runc_exec() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--no-new-privs
|
||||
--tty, -t
|
||||
--detach, -d
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--console
|
||||
--cwd
|
||||
--env, -e
|
||||
--user, -u
|
||||
--process, -p
|
||||
--pid-file
|
||||
--process-label
|
||||
--apparmor
|
||||
--cap, -c
|
||||
"
|
||||
|
||||
local all_options="$options_with_args $boolean_options"
|
||||
|
||||
case "$prev" in
|
||||
--cap | -c)
|
||||
__runc_complete_capabilities
|
||||
return
|
||||
;;
|
||||
|
||||
--console | --cwd | --process | --apparmor)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
--env | -e)
|
||||
COMPREPLY=($(compgen -e -- "$cur"))
|
||||
__runc_nospace
|
||||
return
|
||||
;;
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$all_options" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# global options that may appear after the runc command
|
||||
_runc_runc() {
|
||||
local boolean_options="
|
||||
$global_boolean_options
|
||||
--help
|
||||
--version -v
|
||||
--debug
|
||||
"
|
||||
local options_with_args="
|
||||
--log
|
||||
--log-format
|
||||
--root
|
||||
--criu
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--log | --root | --criu)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
--log-format)
|
||||
COMPREPLY=($(compgen -W 'text json' -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
local counter=$(__runc_pos_first_nonflag $(__runc_to_extglob "$options_with_args"))
|
||||
if [ $cword -eq $counter ]; then
|
||||
COMPREPLY=($(compgen -W "${commands[*]} help" -- "$cur"))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_pause() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_ps() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_delete() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_kill() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
--all
|
||||
-a
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
"kill")
|
||||
__runc_list_all
|
||||
return
|
||||
;;
|
||||
*)
|
||||
__runc_list_signals
|
||||
return
|
||||
;;
|
||||
esac
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_events() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--stats
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--interval
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_list() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--quiet
|
||||
-q
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--format
|
||||
-f
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--format | -f)
|
||||
COMPREPLY=($(compgen -W 'text json' -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
local counter=$(__runc_pos_first_nonflag $(__runc_to_extglob "$options_with_args"))
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_spec() {
|
||||
local boolean_options="
|
||||
--help
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--bundle
|
||||
-b
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--bundle | -b)
|
||||
case "$cur" in
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
local counter=$(__runc_pos_first_nonflag $(__runc_to_extglob "$options_with_args"))
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_run() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--detatch
|
||||
-d
|
||||
--no-subreaper
|
||||
--no-pivot
|
||||
--no-new-keyring
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--bundle
|
||||
-b
|
||||
--console
|
||||
--pid-file
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--bundle | -b | --console | --pid-file)
|
||||
case "$cur" in
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_checkpoint() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
--leave-running
|
||||
--tcp-established
|
||||
--ext-unix-sk
|
||||
--shell-job
|
||||
--file-locks
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--image-path
|
||||
--work-path
|
||||
--page-server
|
||||
--manage-cgroups-mode
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
--page-server) ;;
|
||||
|
||||
--manage-cgroups-mode)
|
||||
COMPREPLY=($(compgen -W "soft full strict" -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
--image-path | --work-path)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_runc_create() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--no-pivot
|
||||
--no-new-keyring
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--bundle
|
||||
-b
|
||||
--console
|
||||
--pid-file
|
||||
"
|
||||
case "$prev" in
|
||||
--bundle | -b | --console | --pid-file)
|
||||
case "$cur" in
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
|
||||
}
|
||||
|
||||
_runc_help() {
|
||||
local counter=$(__runc_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
COMPREPLY=($(compgen -W "${commands[*]}" -- "$cur"))
|
||||
fi
|
||||
}
|
||||
|
||||
_runc_restore() {
|
||||
local boolean_options="
|
||||
--help
|
||||
--tcp-established
|
||||
--ext-unix-sk
|
||||
--shell-job
|
||||
--file-locks
|
||||
--detach
|
||||
-d
|
||||
--no-subreaper
|
||||
--no-pivot
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
-b
|
||||
--bundle
|
||||
--image-path
|
||||
--work-path
|
||||
--manage-cgroups-mode
|
||||
--pid-file
|
||||
"
|
||||
|
||||
local all_options="$options_with_args $boolean_options"
|
||||
|
||||
case "$prev" in
|
||||
--manage-cgroups-mode)
|
||||
COMPREPLY=($(compgen -W "soft full strict" -- "$cur"))
|
||||
return
|
||||
;;
|
||||
|
||||
--pid-file | --image-path | --work-path | --bundle | -b)
|
||||
case "$cur" in
|
||||
*:*) ;; # TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
'')
|
||||
COMPREPLY=($(compgen -W '/' -- "$cur"))
|
||||
__runc_nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
__runc_nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$all_options" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_resume() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc_state() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_runc_start() {
|
||||
local boolean_options="
|
||||
--help
|
||||
-h
|
||||
"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_runc_update() {
|
||||
local boolean_options="
|
||||
--help
|
||||
"
|
||||
|
||||
local options_with_args="
|
||||
--blkio-weight
|
||||
--cpu-period
|
||||
--cpu-quota
|
||||
--cpu-rt-period
|
||||
--cpu-rt-runtime
|
||||
--cpu-share
|
||||
--cpuset-cpus
|
||||
--cpuset-mems
|
||||
--kernel-memory
|
||||
--kernel-memory-tcp
|
||||
--memory
|
||||
--memory-reservation
|
||||
--memory-swap
|
||||
|
||||
"
|
||||
|
||||
case "$prev" in
|
||||
$(__runc_to_extglob "$options_with_args"))
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
|
||||
;;
|
||||
*)
|
||||
__runc_list_all
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_runc() {
|
||||
local previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
|
||||
local commands=(
|
||||
checkpoint
|
||||
create
|
||||
delete
|
||||
events
|
||||
exec
|
||||
init
|
||||
kill
|
||||
list
|
||||
pause
|
||||
ps
|
||||
restore
|
||||
resume
|
||||
run
|
||||
spec
|
||||
start
|
||||
state
|
||||
update
|
||||
help
|
||||
h
|
||||
)
|
||||
|
||||
# These options are valid as global options for all client commands
|
||||
# and valid as command options for `runc daemon`
|
||||
local global_boolean_options="
|
||||
--help -h
|
||||
--version -v
|
||||
"
|
||||
|
||||
COMPREPLY=()
|
||||
local cur prev words cword
|
||||
_get_comp_words_by_ref -n : cur prev words cword
|
||||
|
||||
local command='runc' command_pos=0 subcommand_pos
|
||||
local counter=1
|
||||
while [ $counter -lt $cword ]; do
|
||||
case "${words[$counter]}" in
|
||||
-*) ;;
|
||||
=)
|
||||
((counter++))
|
||||
;;
|
||||
*)
|
||||
command="${words[$counter]}"
|
||||
command_pos=$counter
|
||||
break
|
||||
;;
|
||||
esac
|
||||
((counter++))
|
||||
done
|
||||
|
||||
local completions_func=_runc_${command}
|
||||
declare -F $completions_func >/dev/null && $completions_func
|
||||
|
||||
eval "$previous_extglob_setting"
|
||||
return 0
|
||||
}
|
||||
|
||||
eval "$__runc_previous_extglob_setting"
|
||||
unset __runc_previous_extglob_setting
|
||||
|
||||
complete -F _runc runc
|
314
vendor/github.com/opencontainers/runc/docs/terminals.md
generated
vendored
Normal file
314
vendor/github.com/opencontainers/runc/docs/terminals.md
generated
vendored
Normal file
|
@ -0,0 +1,314 @@
|
|||
# Terminals and Standard IO #
|
||||
|
||||
*Note that the default configuration of `runc` (foreground, new terminal) is
|
||||
generally the best option for most users. This document exists to help explain
|
||||
what the purpose of the different modes is, and to try to steer users away from
|
||||
common mistakes and misunderstandings.*
|
||||
|
||||
In general, most processes on Unix (and Unix-like) operating systems have 3
|
||||
standard file descriptors provided at the start, collectively referred to as
|
||||
"standard IO" (`stdio`):
|
||||
|
||||
* `0`: standard-in (`stdin`), the input stream into the process
|
||||
* `1`: standard-out (`stdout`), the output stream from the process
|
||||
* `2`: standard-error (`stderr`), the error stream from the process
|
||||
|
||||
When creating and running a container via `runc`, it is important to take care
|
||||
to structure the `stdio` the new container's process receives. In some ways
|
||||
containers are just regular processes, while in other ways they're an isolated
|
||||
sub-partition of your machine (in a similar sense to a VM). This means that the
|
||||
structure of IO is not as simple as with ordinary programs (which generally
|
||||
just use the file descriptors you give them).
|
||||
|
||||
## Other File Descriptors ##
|
||||
|
||||
Before we continue, it is important to note that processes can have more file
|
||||
descriptors than just `stdio`. By default in `runc` no other file descriptors
|
||||
will be passed to the spawned container process. If you wish to explicitly pass
|
||||
file descriptors to the container you have to use the `--preserve-fds` option.
|
||||
These ancillary file descriptors don't have any of the strange semantics
|
||||
discussed further in this document (those only apply to `stdio`) -- they are
|
||||
passed untouched by `runc`.
|
||||
|
||||
It should be noted that `--preserve-fds` does not take individual file
|
||||
descriptors to preserve. Instead, it takes how many file descriptors (not
|
||||
including `stdio` or `LISTEN_FDS`) should be passed to the container. In the
|
||||
following example:
|
||||
|
||||
```
|
||||
% runc run --preserve-fds 5 <container>
|
||||
```
|
||||
|
||||
`runc` will pass the first `5` file descriptors (`3`, `4`, `5`, `6`, and `7` --
|
||||
assuming that `LISTEN_FDS` has not been configured) to the container.
|
||||
|
||||
In addition to `--preserve-fds`, `LISTEN_FDS` file descriptors are passed
|
||||
automatically to allow for `systemd`-style socket activation. To extend the
|
||||
above example:
|
||||
|
||||
```
|
||||
% LISTEN_PID=$pid_of_runc LISTEN_FDS=3 runc run --preserve-fds 5 <container>
|
||||
```
|
||||
|
||||
`runc` will now pass the first `8` file descriptors (and it will also pass
|
||||
`LISTEN_FDS=3` and `LISTEN_PID=1` to the container). The first `3` (`3`, `4`,
|
||||
and `5`) were passed due to `LISTEN_FDS` and the other `5` (`6`, `7`, `8`, `9`,
|
||||
and `10`) were passed due to `--preserve-fds`. You should keep this in mind if
|
||||
you use `runc` directly in something like a `systemd` unit file. To disable
|
||||
this `LISTEN_FDS`-style passing just unset `LISTEN_FDS`.
|
||||
|
||||
**Be very careful when passing file descriptors to a container process.** Due
|
||||
to some Linux kernel (mis)features, a container with access to certain types of
|
||||
file descriptors (such as `O_PATH` descriptors) outside of the container's root
|
||||
file system can use these to break out of the container's pivoted mount
|
||||
namespace. [This has resulted in CVEs in the past.][CVE-2016-9962]
|
||||
|
||||
[CVE-2016-9962]: https://nvd.nist.gov/vuln/detail/CVE-2016-9962
|
||||
|
||||
## <a name="terminal-modes" /> Terminal Modes ##
|
||||
|
||||
`runc` supports two distinct methods for passing `stdio` to the container's
|
||||
primary process:
|
||||
|
||||
* [new terminal](#new-terminal) (`terminal: true`)
|
||||
* [pass-through](#pass-through) (`terminal: false`)
|
||||
|
||||
When first using `runc` these two modes will look incredibly similar, but this
|
||||
can be quite deceptive as these different modes have quite different
|
||||
characteristics.
|
||||
|
||||
By default, `runc spec` will create a configuration that will create a new
|
||||
terminal (`terminal: true`). However, if the `terminal: ...` line is not
|
||||
present in `config.json` then pass-through is the default.
|
||||
|
||||
*In general we recommend using new terminal, because it means that tools like
|
||||
`sudo` will work inside your container. But pass-through can be useful if you
|
||||
know what you're doing, or if you're using `runc` as part of a non-interactive
|
||||
pipeline.*
|
||||
|
||||
### <a name="new-terminal"> New Terminal ###
|
||||
|
||||
In new terminal mode, `runc` will create a brand-new "console" (or more
|
||||
precisely, a new pseudo-terminal using the container's namespaced
|
||||
`/dev/pts/ptmx`) for your contained process to use as its `stdio`.
|
||||
|
||||
When you start a process in new terminal mode, `runc` will do the following:
|
||||
|
||||
1. Create a new pseudo-terminal.
|
||||
2. Pass the slave end to the container's primary process as its `stdio`.
|
||||
3. Send the master end to a process to interact with the `stdio` for the
|
||||
container's primary process ([details below](#runc-modes)).
|
||||
|
||||
It should be noted that since a new pseudo-terminal is being used for
|
||||
communication with the container, some strange properties of pseudo-terminals
|
||||
might surprise you. For instance, by default, all new pseudo-terminals
|
||||
translate the byte `'\n'` to the sequence `'\r\n'` on both `stdout` and
|
||||
`stderr`. In addition there are [a whole range of `ioctls(2)` that can only
|
||||
interact with pseudo-terminal `stdio`][tty_ioctl(4)].
|
||||
|
||||
> **NOTE**: In new terminal mode, all three `stdio` file descriptors are the
|
||||
> same underlying file. The reason for this is to match how a shell's `stdio`
|
||||
> looks to a process (as well as remove race condition issues with having to
|
||||
> deal with multiple master pseudo-terminal file descriptors). However this
|
||||
> means that it is not really possible to uniquely distinguish between `stdout`
|
||||
> and `stderr` from the caller's perspective.
|
||||
|
||||
[tty_ioctl(4)]: https://linux.die.net/man/4/tty_ioctl
|
||||
|
||||
### <a name="pass-through"> Pass-Through ###
|
||||
|
||||
If you have already set up some file handles that you wish your contained
|
||||
process to use as its `stdio`, then you can ask `runc` to pass them through to
|
||||
the contained process (this is not necessarily the same as `--preserve-fds`'s
|
||||
passing of file descriptors -- [details below](#runc-modes)). As an example
|
||||
(assuming that `terminal: false` is set in `config.json`):
|
||||
|
||||
```
|
||||
% echo input | runc run some_container > /tmp/log.out 2>& /tmp/log.err
|
||||
```
|
||||
|
||||
Here the container's various `stdio` file descriptors will be substituted with
|
||||
the following:
|
||||
|
||||
* `stdin` will be sourced from the `echo input` pipeline.
|
||||
* `stdout` will be output into `/tmp/log.out` on the host.
|
||||
* `stderr` will be output into `/tmp/log.err` on the host.
|
||||
|
||||
It should be noted that the actual file handles seen inside the container may
|
||||
be different [based on the mode `runc` is being used in](#runc-modes) (for
|
||||
instance, the file referenced by `1` could be `/tmp/log.out` directly or a pipe
|
||||
which `runc` is using to buffer output, based on the mode). However the net
|
||||
result will be the same in either case. In principle you could use the [new
|
||||
terminal mode](#new-terminal) in a pipeline, but the difference will become
|
||||
more clear when you are introduced to [`runc`'s detached mode](#runc-modes).
|
||||
|
||||
## <a name="runc-modes" /> `runc` Modes ##
|
||||
|
||||
`runc` itself runs in two modes:
|
||||
|
||||
* [foreground](#foreground)
|
||||
* [detached](#detached)
|
||||
|
||||
You can use either [terminal mode](#terminal-modes) with either `runc` mode.
|
||||
However, there are considerations that may indicate preference for one mode
|
||||
over another. It should be noted that while two types of modes (terminal and
|
||||
`runc`) are conceptually independent from each other, you should be aware of
|
||||
the intricacies of which combination you are using.
|
||||
|
||||
*In general we recommend using foreground because it's the most
|
||||
straight-forward to use, with the only downside being that you will have a
|
||||
long-running `runc` process. Detached mode is difficult to get right and
|
||||
generally requires having your own `stdio` management.*
|
||||
|
||||
### Foreground ###
|
||||
|
||||
The default (and most straight-forward) mode of `runc`. In this mode, your
|
||||
`runc` command remains in the foreground with the container process as a child.
|
||||
All `stdio` is buffered through the foreground `runc` process (irrespective of
|
||||
which terminal mode you are using). This is conceptually quite similar to
|
||||
running a normal process interactively in a shell (and if you are using `runc`
|
||||
in a shell interactively, this is what you should use).
|
||||
|
||||
Because the `stdio` will be buffered in this mode, some very important
|
||||
peculiarities of this mode should be kept in mind:
|
||||
|
||||
* With [new terminal mode](#new-terminal), the container will see a
|
||||
pseudo-terminal as its `stdio` (as you might expect). However, the `stdio` of
|
||||
the foreground `runc` process will remain the `stdio` that the process was
|
||||
started with -- and `runc` will copy all `stdio` between its `stdio` and the
|
||||
container's `stdio`. This means that while a new pseudo-terminal has been
|
||||
created, the foreground `runc` process manages it over the lifetime of the
|
||||
container.
|
||||
|
||||
* With [pass-through mode](#pass-through), the foreground `runc`'s `stdio` is
|
||||
**not** passed to the container. Instead, the container's `stdio` is a set of
|
||||
pipes which are used to copy data between `runc`'s `stdio` and the
|
||||
container's `stdio`. This means that the container never has direct access to
|
||||
host file descriptors (aside from the pipes created by the container runtime,
|
||||
but that shouldn't be an issue).
|
||||
|
||||
The main drawback of the foreground mode of operation is that it requires a
|
||||
long-running foreground `runc` process. If you kill the foreground `runc`
|
||||
process then you will no longer have access to the `stdio` of the container
|
||||
(and in most cases this will result in the container dying abnormally due to
|
||||
`SIGPIPE` or some other error). By extension this means that any bug in the
|
||||
long-running foreground `runc` process (such as a memory leak) or a stray
|
||||
OOM-kill sweep could result in your container being killed **through no fault
|
||||
of the user**. In addition, there is no way in foreground mode of passing a
|
||||
file descriptor directly to the container process as its `stdio` (like
|
||||
`--preserve-fds` does).
|
||||
|
||||
These shortcomings are obviously sub-optimal and are the reason that `runc` has
|
||||
an additional mode called "detached mode".
|
||||
|
||||
### Detached ###
|
||||
|
||||
In contrast to foreground mode, in detached mode there is no long-running
|
||||
foreground `runc` process once the container has started. In fact, there is no
|
||||
long-running `runc` process at all. However, this means that it is up to the
|
||||
caller to handle the `stdio` after `runc` has set it up for you. In a shell
|
||||
this means that the `runc` command will exit and control will return to the
|
||||
shell, after the container has been set up.
|
||||
|
||||
You can run `runc` in detached mode in one of the following ways:
|
||||
|
||||
* `runc run -d ...` which operates similar to `runc run` but is detached.
|
||||
* `runc create` followed by `runc start` which is the standard container
|
||||
lifecycle defined by the OCI runtime specification (`runc create` sets up the
|
||||
container completely, waiting for `runc start` to begin execution of user
|
||||
code).
|
||||
|
||||
The main use-case of detached mode is for higher-level tools that want to be
|
||||
wrappers around `runc`. By running `runc` in detached mode, those tools have
|
||||
far more control over the container's `stdio` without `runc` getting in the
|
||||
way (most wrappers around `runc` like `cri-o` or `containerd` use detached mode
|
||||
for this reason).
|
||||
|
||||
Unfortunately using detached mode is a bit more complicated and requires more
|
||||
care than the foreground mode -- mainly because it is now up to the caller to
|
||||
handle the `stdio` of the container.
|
||||
|
||||
#### Detached Pass-Through ####
|
||||
|
||||
In detached mode, pass-through actually does what it says on the tin -- the
|
||||
`stdio` file descriptors of the `runc` process are passed through (untouched)
|
||||
to the container's `stdio`. The purpose of this option is to allow a user to
|
||||
set up `stdio` for a container themselves and then force `runc` to just use
|
||||
their pre-prepared `stdio` (without any pseudo-terminal funny business). *If
|
||||
you don't see why this would be useful, don't use this option.*
|
||||
|
||||
**You must be incredibly careful when using detached pass-through (especially
|
||||
in a shell).** The reason for this is that by using detached pass-through you
|
||||
are passing host file descriptors to the container. In the case of a shell,
|
||||
usually your `stdio` is going to be a pseudo-terminal (on your host). A
|
||||
malicious container could take advantage of TTY-specific `ioctls` like
|
||||
`TIOCSTI` to fake input into the **host** shell (remember that in detached
|
||||
mode, control is returned to your shell and so the terminal you've given the
|
||||
container is being read by a shell prompt).
|
||||
|
||||
There are also several other issues with running non-malicious containers in a
|
||||
shell with detached pass-through (where you pass your shell's `stdio` to the
|
||||
container):
|
||||
|
||||
* Output from the container will be interleaved with output from your shell (in
|
||||
a non-deterministic way), without any real way of distinguishing from where a
|
||||
particular piece of output came from.
|
||||
|
||||
* Any input to `stdin` will be non-deterministically split and given to either
|
||||
the container or the shell (because both are blocked on a `read(2)` of the
|
||||
same FIFO-style file descriptor).
|
||||
|
||||
They are all related to the fact that there is going to be a race when either
|
||||
your host or the container tries to read from (or write to) `stdio`. This
|
||||
problem is especially obvious when in a shell, where usually the terminal has
|
||||
been put into raw mode (where each individual key-press should cause `read(2)`
|
||||
to return).
|
||||
|
||||
> **NOTE**: There is also currently a [known problem][issue-1721] where using
|
||||
> detached pass-through will result in the container hanging if the `stdout` or
|
||||
> `stderr` is a pipe (though this should be a temporary issue).
|
||||
|
||||
[issue-1721]: https://github.com/opencontainers/runc/issues/1721
|
||||
|
||||
#### Detached New Terminal ####
|
||||
|
||||
When creating a new pseudo-terminal in detached mode, and fairly obvious
|
||||
problem appears -- how do we use the new terminal that `runc` created? Unlike
|
||||
in pass-through, `runc` has created a new set of file descriptors that need to
|
||||
be used by *something* in order for container communication to work.
|
||||
|
||||
The way this problem is resolved is through the use of Unix domain sockets.
|
||||
There is a feature of Unix sockets called `SCM_RIGHTS` which allows a file
|
||||
descriptor to be sent through a Unix socket to a completely separate process
|
||||
(which can then use that file descriptor as though they opened it). When using
|
||||
`runc` in detached new terminal mode, this is how a user gets access to the
|
||||
pseudo-terminal's master file descriptor.
|
||||
|
||||
To this end, there is a new option (which is required if you want to use `runc`
|
||||
in detached new terminal mode): `--console-socket`. This option takes the path
|
||||
to a Unix domain socket which `runc` will connect to and send the
|
||||
pseudo-terminal master file descriptor down. The general process for getting
|
||||
the pseudo-terminal master is as follows:
|
||||
|
||||
1. Create a Unix domain socket at some path, `$socket_path`.
|
||||
2. Call `runc run` or `runc create` with the argument `--console-socket
|
||||
$socket_path`.
|
||||
3. Using `recvmsg(2)` retrieve the file descriptor sent using `SCM_RIGHTS` by
|
||||
`runc`.
|
||||
4. Now the manager can interact with the `stdio` of the container, using the
|
||||
retrieved pseudo-terminal master.
|
||||
|
||||
After `runc` exits, the only process with a copy of the pseudo-terminal master
|
||||
file descriptor is whoever read the file descriptor from the socket.
|
||||
|
||||
> **NOTE**: Currently `runc` doesn't support abstract socket addresses (due to
|
||||
> it not being possible to pass an `argv` with a null-byte as the first
|
||||
> character). In the future this may change, but currently you must use a valid
|
||||
> path name.
|
||||
|
||||
In order to help users make use of detached new terminal mode, we have provided
|
||||
a [Go implementation in the `go-runc` bindings][containerd/go-runc.Socket], as
|
||||
well as [a simple client][recvtty].
|
||||
|
||||
[containerd/go-runc.Socket]: https://godoc.org/github.com/containerd/go-runc#Socket
|
||||
[recvtty]: /contrib/cmd/recvtty
|
1
vendor/github.com/opencontainers/runc/exec.go
generated
vendored
1
vendor/github.com/opencontainers/runc/exec.go
generated
vendored
|
@ -140,6 +140,7 @@ func execProcess(context *cli.Context) (int, error) {
|
|||
detach: detach,
|
||||
pidFile: context.String("pid-file"),
|
||||
action: CT_ACT_RUN,
|
||||
init: false,
|
||||
}
|
||||
return r.run(p)
|
||||
}
|
||||
|
|
54
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
54
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go
generated
vendored
|
@ -3,7 +3,6 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -14,6 +13,8 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -35,7 +36,7 @@ var (
|
|||
HugePageSizes, _ = cgroups.GetHugePageSize()
|
||||
)
|
||||
|
||||
var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")
|
||||
var errSubsystemDoesNotExist = fmt.Errorf("cgroup: subsystem does not exist")
|
||||
|
||||
type subsystemSet []subsystem
|
||||
|
||||
|
@ -62,9 +63,10 @@ type subsystem interface {
|
|||
}
|
||||
|
||||
type Manager struct {
|
||||
mu sync.Mutex
|
||||
Cgroups *configs.Cgroup
|
||||
Paths map[string]string
|
||||
mu sync.Mutex
|
||||
Cgroups *configs.Cgroup
|
||||
Rootless bool
|
||||
Paths map[string]string
|
||||
}
|
||||
|
||||
// The absolute path to the root of the cgroup hierarchies.
|
||||
|
@ -100,6 +102,33 @@ type cgroupData struct {
|
|||
pid int
|
||||
}
|
||||
|
||||
// isIgnorableError returns whether err is a permission error (in the loose
|
||||
// sense of the word). This includes EROFS (which for an unprivileged user is
|
||||
// basically a permission error) and EACCES (for similar reasons) as well as
|
||||
// the normal EPERM.
|
||||
func isIgnorableError(rootless bool, err error) bool {
|
||||
// We do not ignore errors if we are root.
|
||||
if !rootless {
|
||||
return false
|
||||
}
|
||||
// Is it an ordinary EPERM?
|
||||
if os.IsPermission(errors.Cause(err)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Try to handle other errnos.
|
||||
var errno error
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *os.PathError:
|
||||
errno = err.Err
|
||||
case *os.LinkError:
|
||||
errno = err.Err
|
||||
case *os.SyscallError:
|
||||
errno = err.Err
|
||||
}
|
||||
return errno == unix.EROFS || errno == unix.EPERM || errno == unix.EACCES
|
||||
}
|
||||
|
||||
func (m *Manager) Apply(pid int) (err error) {
|
||||
if m.Cgroups == nil {
|
||||
return nil
|
||||
|
@ -145,11 +174,11 @@ func (m *Manager) Apply(pid int) (err error) {
|
|||
m.Paths[sys.Name()] = p
|
||||
|
||||
if err := sys.Apply(d); err != nil {
|
||||
if os.IsPermission(err) && m.Cgroups.Path == "" {
|
||||
// If we didn't set a cgroup path, then let's defer the error here
|
||||
// until we know whether we have set limits or not.
|
||||
// If we hadn't set limits, then it's ok that we couldn't join this cgroup, because
|
||||
// it will have the same limits as its parent.
|
||||
// In the case of rootless, where an explicit cgroup path hasn't
|
||||
// been set, we don't bail on error in case of permission problems.
|
||||
// Cases where limits have been set (and we couldn't create our own
|
||||
// cgroup) are handled by Set.
|
||||
if isIgnorableError(m.Rootless, err) && m.Cgroups.Path == "" {
|
||||
delete(m.Paths, sys.Name())
|
||||
continue
|
||||
}
|
||||
|
@ -208,8 +237,9 @@ func (m *Manager) Set(container *configs.Config) error {
|
|||
path := paths[sys.Name()]
|
||||
if err := sys.Set(path, container.Cgroups); err != nil {
|
||||
if path == "" {
|
||||
// cgroup never applied
|
||||
return fmt.Errorf("cannot set limits on the %s cgroup, as the container has not joined it", sys.Name())
|
||||
// We never created a path for this cgroup, so we cannot set
|
||||
// limits for it (though we have already tried at this point).
|
||||
return fmt.Errorf("cannot set %s limit: container could not join or create cgroup", sys.Name())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go
generated
vendored
|
@ -77,7 +77,7 @@ func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) erro
|
|||
// The logic is, if user specified cpuset configs, use these
|
||||
// specified configs, otherwise, inherit from parent. This makes
|
||||
// cpuset configs work correctly with 'cpuset.cpu_exclusive', and
|
||||
// keep backward compatbility.
|
||||
// keep backward compatibility.
|
||||
if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/stats_util_test.go
generated
vendored
|
@ -86,7 +86,7 @@ func expectMemoryStatEquals(t *testing.T, expected, actual cgroups.MemoryStats)
|
|||
expectMemoryDataEquals(t, expected.KernelUsage, actual.KernelUsage)
|
||||
|
||||
if expected.UseHierarchy != actual.UseHierarchy {
|
||||
logrus.Printf("Expected memory use hiearchy %v, but found %v\n", expected.UseHierarchy, actual.UseHierarchy)
|
||||
logrus.Printf("Expected memory use hierarchy %v, but found %v\n", expected.UseHierarchy, actual.UseHierarchy)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
|
|
77
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
77
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/apply_systemd.go
generated
vendored
|
@ -5,6 +5,7 @@ package systemd
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -75,7 +76,8 @@ var (
|
|||
hasStartTransientUnit bool
|
||||
hasStartTransientSliceUnit bool
|
||||
hasTransientDefaultDependencies bool
|
||||
hasDelegate bool
|
||||
hasDelegateScope bool
|
||||
hasDelegateSlice bool
|
||||
)
|
||||
|
||||
func newProp(name string, units interface{}) systemdDbus.Property {
|
||||
|
@ -150,12 +152,12 @@ func UseSystemd() bool {
|
|||
theConn.StopUnit(scope, "replace", nil)
|
||||
|
||||
// Assume StartTransientUnit on a scope allows Delegate
|
||||
hasDelegate = true
|
||||
dl := newProp("Delegate", true)
|
||||
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dl}, nil); err != nil {
|
||||
hasDelegateScope = true
|
||||
dlScope := newProp("Delegate", true)
|
||||
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dlScope}, nil); err != nil {
|
||||
if dbusError, ok := err.(dbus.Error); ok {
|
||||
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
|
||||
hasDelegate = false
|
||||
hasDelegateScope = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +189,22 @@ func UseSystemd() bool {
|
|||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
// Not critical because of the stop unit logic above.
|
||||
theConn.StopUnit(slice, "replace", nil)
|
||||
|
||||
// Assume StartTransientUnit on a slice allows Delegate
|
||||
hasDelegateSlice = true
|
||||
dlSlice := newProp("Delegate", true)
|
||||
if _, err := theConn.StartTransientUnit(slice, "replace", []systemdDbus.Property{dlSlice}, nil); err != nil {
|
||||
if dbusError, ok := err.(dbus.Error); ok {
|
||||
// Starting with systemd v237, Delegate is not even a property of slices anymore,
|
||||
// so the D-Bus call fails with "InvalidArgs" error.
|
||||
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") {
|
||||
hasDelegateSlice = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not critical because of the stop unit logic above.
|
||||
theConn.StopUnit(scope, "replace", nil)
|
||||
theConn.StopUnit(slice, "replace", nil)
|
||||
|
@ -242,9 +260,16 @@ func (m *Manager) Apply(pid int) error {
|
|||
properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
|
||||
}
|
||||
|
||||
if hasDelegate {
|
||||
// This is only supported on systemd versions 218 and above.
|
||||
properties = append(properties, newProp("Delegate", true))
|
||||
// Check if we can delegate. This is only supported on systemd versions 218 and above.
|
||||
if strings.HasSuffix(unitName, ".slice") {
|
||||
if hasDelegateSlice {
|
||||
// systemd 237 and above no longer allows delegation on a slice
|
||||
properties = append(properties, newProp("Delegate", true))
|
||||
}
|
||||
} else {
|
||||
if hasDelegateScope {
|
||||
properties = append(properties, newProp("Delegate", true))
|
||||
}
|
||||
}
|
||||
|
||||
// Always enable accounting, this gets us the same behaviour as the fs implementation,
|
||||
|
@ -271,13 +296,19 @@ func (m *Manager) Apply(pid int) error {
|
|||
|
||||
// cpu.cfs_quota_us and cpu.cfs_period_us are controlled by systemd.
|
||||
if c.Resources.CpuQuota != 0 && c.Resources.CpuPeriod != 0 {
|
||||
cpuQuotaPerSecUSec := uint64(c.Resources.CpuQuota*1000000) / c.Resources.CpuPeriod
|
||||
// systemd converts CPUQuotaPerSecUSec (microseconds per CPU second) to CPUQuota
|
||||
// (integer percentage of CPU) internally. This means that if a fractional percent of
|
||||
// CPU is indicated by Resources.CpuQuota, we need to round up to the nearest
|
||||
// 10ms (1% of a second) such that child cgroups can set the cpu.cfs_quota_us they expect.
|
||||
if cpuQuotaPerSecUSec%10000 != 0 {
|
||||
cpuQuotaPerSecUSec = ((cpuQuotaPerSecUSec / 10000) + 1) * 10000
|
||||
// corresponds to USEC_INFINITY in systemd
|
||||
// if USEC_INFINITY is provided, CPUQuota is left unbound by systemd
|
||||
// always setting a property value ensures we can apply a quota and remove it later
|
||||
cpuQuotaPerSecUSec := uint64(math.MaxUint64)
|
||||
if c.Resources.CpuQuota > 0 {
|
||||
// systemd converts CPUQuotaPerSecUSec (microseconds per CPU second) to CPUQuota
|
||||
// (integer percentage of CPU) internally. This means that if a fractional percent of
|
||||
// CPU is indicated by Resources.CpuQuota, we need to round up to the nearest
|
||||
// 10ms (1% of a second) such that child cgroups can set the cpu.cfs_quota_us they expect.
|
||||
cpuQuotaPerSecUSec = uint64(c.Resources.CpuQuota*1000000) / c.Resources.CpuPeriod
|
||||
if cpuQuotaPerSecUSec%10000 != 0 {
|
||||
cpuQuotaPerSecUSec = ((cpuQuotaPerSecUSec / 10000) + 1) * 10000
|
||||
}
|
||||
}
|
||||
properties = append(properties,
|
||||
newProp("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec))
|
||||
|
@ -296,17 +327,17 @@ func (m *Manager) Apply(pid int) error {
|
|||
}
|
||||
}
|
||||
|
||||
statusChan := make(chan string)
|
||||
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err != nil && !isUnitExists(err) {
|
||||
statusChan := make(chan string, 1)
|
||||
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err == nil {
|
||||
select {
|
||||
case <-statusChan:
|
||||
case <-time.After(time.Second):
|
||||
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", unitName)
|
||||
}
|
||||
} else if !isUnitExists(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-statusChan:
|
||||
case <-time.After(time.Second):
|
||||
logrus.Warnf("Timed out while waiting for StartTransientUnit completion signal from dbus. Continuing...")
|
||||
}
|
||||
|
||||
if err := joinCgroups(c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
25
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
25
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
generated
vendored
|
@ -13,7 +13,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -103,7 +103,7 @@ func FindCgroupMountpointDir() (string, error) {
|
|||
}
|
||||
|
||||
if postSeparatorFields[0] == "cgroup" {
|
||||
// Check that the mount is properly formated.
|
||||
// Check that the mount is properly formatted.
|
||||
if numPostFields < 3 {
|
||||
return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||
}
|
||||
|
@ -151,19 +151,20 @@ func getCgroupMountsHelper(ss map[string]bool, mi io.Reader, all bool) ([]Mount,
|
|||
Root: fields[3],
|
||||
}
|
||||
for _, opt := range strings.Split(fields[len(fields)-1], ",") {
|
||||
if !ss[opt] {
|
||||
seen, known := ss[opt]
|
||||
if !known || (!all && seen) {
|
||||
continue
|
||||
}
|
||||
ss[opt] = true
|
||||
if strings.HasPrefix(opt, cgroupNamePrefix) {
|
||||
m.Subsystems = append(m.Subsystems, opt[len(cgroupNamePrefix):])
|
||||
} else {
|
||||
m.Subsystems = append(m.Subsystems, opt)
|
||||
}
|
||||
if !all {
|
||||
numFound++
|
||||
opt = opt[len(cgroupNamePrefix):]
|
||||
}
|
||||
m.Subsystems = append(m.Subsystems, opt)
|
||||
numFound++
|
||||
}
|
||||
if len(m.Subsystems) > 0 || all {
|
||||
res = append(res, m)
|
||||
}
|
||||
res = append(res, m)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
|
@ -187,7 +188,7 @@ func GetCgroupMounts(all bool) ([]Mount, error) {
|
|||
|
||||
allMap := make(map[string]bool)
|
||||
for s := range allSubsystems {
|
||||
allMap[s] = true
|
||||
allMap[s] = false
|
||||
}
|
||||
return getCgroupMountsHelper(allMap, f, all)
|
||||
}
|
||||
|
@ -262,7 +263,7 @@ func getCgroupPathHelper(subsystem, cgroup string) (string, error) {
|
|||
}
|
||||
|
||||
// This is needed for nested containers, because in /proc/self/cgroup we
|
||||
// see pathes from host, which don't exist in container.
|
||||
// see paths from host, which don't exist in container.
|
||||
relCgroup, err := filepath.Rel(root, cgroup)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
151
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils_test.go
generated
vendored
151
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils_test.go
generated
vendored
|
@ -93,6 +93,62 @@ const systemdMountinfo = `115 83 0:32 / / rw,relatime - aufs none rw,si=c0bd3d3,
|
|||
136 117 0:12 /1 /dev/console rw,nosuid,noexec,relatime - devpts none rw,gid=5,mode=620,ptmxmode=000
|
||||
84 115 0:40 / /tmp rw,relatime - tmpfs none rw`
|
||||
|
||||
const bedrockMountinfo = `120 17 0:28 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
124 28 0:28 / /bedrock/strata/arch/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
123 53 0:28 / /bedrock/strata/fallback/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
122 71 0:28 / /bedrock/strata/gentoo/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
121 89 0:28 / /bedrock/strata/kde/sys/fs/cgroup rw,nosuid,nodev,noexec shared:16 - tmpfs tmpfs ro,mode=755
|
||||
125 120 0:29 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
129 124 0:29 / /bedrock/strata/arch/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
128 123 0:29 / /bedrock/strata/fallback/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
127 122 0:29 / /bedrock/strata/gentoo/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
126 121 0:29 / /bedrock/strata/kde/sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
|
||||
140 120 0:32 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
144 124 0:32 / /bedrock/strata/arch/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
143 123 0:32 / /bedrock/strata/fallback/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
142 122 0:32 / /bedrock/strata/gentoo/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
141 121 0:32 / /bedrock/strata/kde/sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:48 - cgroup cgroup rw,net_cls,net_prio
|
||||
145 120 0:33 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
149 124 0:33 / /bedrock/strata/arch/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
148 123 0:33 / /bedrock/strata/fallback/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
147 122 0:33 / /bedrock/strata/gentoo/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
146 121 0:33 / /bedrock/strata/kde/sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:49 - cgroup cgroup rw,blkio
|
||||
150 120 0:34 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
154 124 0:34 / /bedrock/strata/arch/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
153 123 0:34 / /bedrock/strata/fallback/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
152 122 0:34 / /bedrock/strata/gentoo/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
151 121 0:34 / /bedrock/strata/kde/sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:50 - cgroup cgroup rw,cpu,cpuacct
|
||||
155 120 0:35 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
159 124 0:35 / /bedrock/strata/arch/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
158 123 0:35 / /bedrock/strata/fallback/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
157 122 0:35 / /bedrock/strata/gentoo/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
156 121 0:35 / /bedrock/strata/kde/sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:51 - cgroup cgroup rw,cpuset
|
||||
160 120 0:36 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
164 124 0:36 / /bedrock/strata/arch/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
163 123 0:36 / /bedrock/strata/fallback/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
162 122 0:36 / /bedrock/strata/gentoo/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
161 121 0:36 / /bedrock/strata/kde/sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:52 - cgroup cgroup rw,devices
|
||||
165 120 0:37 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
169 124 0:37 / /bedrock/strata/arch/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
168 123 0:37 / /bedrock/strata/fallback/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
167 122 0:37 / /bedrock/strata/gentoo/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
166 121 0:37 / /bedrock/strata/kde/sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:53 - cgroup cgroup rw,memory
|
||||
170 120 0:38 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
174 124 0:38 / /bedrock/strata/arch/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
173 123 0:38 / /bedrock/strata/fallback/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
172 122 0:38 / /bedrock/strata/gentoo/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
171 121 0:38 / /bedrock/strata/kde/sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:54 - cgroup cgroup rw,freezer
|
||||
175 120 0:39 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
179 124 0:39 / /bedrock/strata/arch/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
178 123 0:39 / /bedrock/strata/fallback/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
177 122 0:39 / /bedrock/strata/gentoo/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
176 121 0:39 / /bedrock/strata/kde/sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:55 - cgroup cgroup rw,pids
|
||||
180 120 0:40 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
184 124 0:40 / /bedrock/strata/arch/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
183 123 0:40 / /bedrock/strata/fallback/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
182 122 0:40 / /bedrock/strata/gentoo/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event
|
||||
181 121 0:40 / /bedrock/strata/kde/sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:56 - cgroup cgroup rw,perf_event`
|
||||
|
||||
const cgroup2Mountinfo = `18 64 0:18 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
|
||||
19 64 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
|
||||
20 64 0:6 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=8171204k,nr_inodes=2042801,mode=755
|
||||
|
@ -132,31 +188,46 @@ func TestGetCgroupMounts(t *testing.T) {
|
|||
mountInfo: fedoraMountinfo,
|
||||
root: "/",
|
||||
subsystems: map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"hugetlb": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
"hugetlb": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
mountInfo: systemdMountinfo,
|
||||
root: "/system.slice/docker-dc4eaa1a34ec4d593bc0125d31eea823a1d76ae483aeb1409cca80304e34da2e.scope",
|
||||
subsystems: map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
mountInfo: bedrockMountinfo,
|
||||
root: "/",
|
||||
subsystems: map[string]bool{
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -199,16 +270,16 @@ func TestGetCgroupMounts(t *testing.T) {
|
|||
|
||||
func BenchmarkGetCgroupMounts(b *testing.B) {
|
||||
subsystems := map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"hugetlb": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
"hugetlb": false,
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@ -276,17 +347,17 @@ func TestParseCgroupString(t *testing.T) {
|
|||
|
||||
func TestIgnoreCgroup2Mount(t *testing.T) {
|
||||
subsystems := map[string]bool{
|
||||
"cpuset": true,
|
||||
"cpu": true,
|
||||
"cpuacct": true,
|
||||
"memory": true,
|
||||
"devices": true,
|
||||
"freezer": true,
|
||||
"net_cls": true,
|
||||
"blkio": true,
|
||||
"perf_event": true,
|
||||
"pids": true,
|
||||
"name=systemd": true,
|
||||
"cpuset": false,
|
||||
"cpu": false,
|
||||
"cpuacct": false,
|
||||
"memory": false,
|
||||
"devices": false,
|
||||
"freezer": false,
|
||||
"net_cls": false,
|
||||
"blkio": false,
|
||||
"perf_event": false,
|
||||
"pids": false,
|
||||
"name=systemd": false,
|
||||
}
|
||||
|
||||
mi := bytes.NewBufferString(cgroup2Mountinfo)
|
||||
|
|
5
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
5
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
generated
vendored
|
@ -141,9 +141,10 @@ type Config struct {
|
|||
|
||||
// OomScoreAdj specifies the adjustment to be made by the kernel when calculating oom scores
|
||||
// for a process. Valid values are between the range [-1000, '1000'], where processes with
|
||||
// higher scores are preferred for being killed.
|
||||
// higher scores are preferred for being killed. If it is unset then we don't touch the current
|
||||
// value.
|
||||
// More information about kernel oom score calculation here: https://lwn.net/Articles/317814/
|
||||
OomScoreAdj int `json:"oom_score_adj"`
|
||||
OomScoreAdj *int `json:"oom_score_adj,omitempty"`
|
||||
|
||||
// UidMappings is an array of User ID mappings for User Namespaces
|
||||
UidMappings []IDMap `json:"uid_mappings"`
|
||||
|
|
13
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/rootless.go
generated
vendored
13
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/rootless.go
generated
vendored
|
@ -43,13 +43,12 @@ func rootlessMappings(config *configs.Config) error {
|
|||
if !config.Namespaces.Contains(configs.NEWUSER) {
|
||||
return fmt.Errorf("rootless containers require user namespaces")
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.UidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one UID mapping")
|
||||
}
|
||||
if len(config.GidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one UID mapping")
|
||||
if len(config.UidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one UID mapping")
|
||||
}
|
||||
if len(config.GidMappings) == 0 {
|
||||
return fmt.Errorf("rootless containers requires at least one GID mapping")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
10
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go
generated
vendored
10
vendor/github.com/opencontainers/runc/libcontainer/configs/validate/validator.go
generated
vendored
|
@ -151,6 +151,16 @@ func (v *ConfigValidator) sysctl(config *configs.Config) error {
|
|||
return fmt.Errorf("sysctl %q is not allowed in the hosts network namespace", s)
|
||||
}
|
||||
}
|
||||
if config.Namespaces.Contains(configs.NEWUTS) {
|
||||
switch s {
|
||||
case "kernel.domainname":
|
||||
// This is namespaced and there's no explicit OCI field for it.
|
||||
continue
|
||||
case "kernel.hostname":
|
||||
// This is namespaced but there's a conflicting (dedicated) OCI field for it.
|
||||
return fmt.Errorf("sysctl %q is not allowed as it conflicts with the OCI %q field", s, "hostname")
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("sysctl %q is not in a separate kernel namespace", s)
|
||||
}
|
||||
|
||||
|
|
146
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
146
vendor/github.com/opencontainers/runc/libcontainer/container_linux.go
generated
vendored
|
@ -28,7 +28,6 @@ import (
|
|||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -225,17 +224,13 @@ func (c *linuxContainer) Set(config configs.Config) error {
|
|||
func (c *linuxContainer) Start(process *Process) error {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
status, err := c.currentStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status == Stopped {
|
||||
if process.Init {
|
||||
if err := c.createExecFifo(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := c.start(process, status == Stopped); err != nil {
|
||||
if status == Stopped {
|
||||
if err := c.start(process); err != nil {
|
||||
if process.Init {
|
||||
c.deleteExecFifo()
|
||||
}
|
||||
return err
|
||||
|
@ -244,17 +239,10 @@ func (c *linuxContainer) Start(process *Process) error {
|
|||
}
|
||||
|
||||
func (c *linuxContainer) Run(process *Process) error {
|
||||
c.m.Lock()
|
||||
status, err := c.currentStatus()
|
||||
if err != nil {
|
||||
c.m.Unlock()
|
||||
return err
|
||||
}
|
||||
c.m.Unlock()
|
||||
if err := c.Start(process); err != nil {
|
||||
return err
|
||||
}
|
||||
if status == Stopped {
|
||||
if process.Init {
|
||||
return c.exec()
|
||||
}
|
||||
return nil
|
||||
|
@ -335,8 +323,8 @@ type openResult struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (c *linuxContainer) start(process *Process, isInit bool) error {
|
||||
parent, err := c.newParentProcess(process, isInit)
|
||||
func (c *linuxContainer) start(process *Process) error {
|
||||
parent, err := c.newParentProcess(process)
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "creating new parent process")
|
||||
}
|
||||
|
@ -349,7 +337,7 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|||
}
|
||||
// generate a timestamp indicating when the container was started
|
||||
c.created = time.Now().UTC()
|
||||
if isInit {
|
||||
if process.Init {
|
||||
c.state = &createdState{
|
||||
c: c,
|
||||
}
|
||||
|
@ -377,10 +365,6 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.state = &runningState{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -443,7 +427,7 @@ func (c *linuxContainer) includeExecFifo(cmd *exec.Cmd) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error) {
|
||||
func (c *linuxContainer) newParentProcess(p *Process) (parentProcess, error) {
|
||||
parentPipe, childPipe, err := utils.NewSockPair("init")
|
||||
if err != nil {
|
||||
return nil, newSystemErrorWithCause(err, "creating new init pipe")
|
||||
|
@ -452,7 +436,7 @@ func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProces
|
|||
if err != nil {
|
||||
return nil, newSystemErrorWithCause(err, "creating new command template")
|
||||
}
|
||||
if !doInit {
|
||||
if !p.Init {
|
||||
return c.newSetnsProcess(p, cmd, parentPipe, childPipe)
|
||||
}
|
||||
|
||||
|
@ -477,6 +461,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
|
|||
if cmd.SysProcAttr == nil {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GOMAXPROCS=%s", os.Getenv("GOMAXPROCS")))
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, p.ExtraFiles...)
|
||||
if p.ConsoleSocket != nil {
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, p.ConsoleSocket)
|
||||
|
@ -672,7 +657,7 @@ func (c *linuxContainer) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.
|
|||
Features: criuFeat,
|
||||
}
|
||||
|
||||
err := c.criuSwrk(nil, req, criuOpts, false)
|
||||
err := c.criuSwrk(nil, req, criuOpts, false, nil)
|
||||
if err != nil {
|
||||
logrus.Debugf("%s", err)
|
||||
return fmt.Errorf("CRIU feature check failed")
|
||||
|
@ -785,7 +770,7 @@ func (c *linuxContainer) checkCriuVersion(minVersion int) error {
|
|||
Type: &t,
|
||||
}
|
||||
|
||||
err := c.criuSwrk(nil, req, nil, false)
|
||||
err := c.criuSwrk(nil, req, nil, false, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CRIU version check failed: %s", err)
|
||||
}
|
||||
|
@ -943,6 +928,33 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
LazyPages: proto.Bool(criuOpts.LazyPages),
|
||||
}
|
||||
|
||||
// If the container is running in a network namespace and has
|
||||
// a path to the network namespace configured, we will dump
|
||||
// that network namespace as an external namespace and we
|
||||
// will expect that the namespace exists during restore.
|
||||
// This basically means that CRIU will ignore the namespace
|
||||
// and expect to be setup correctly.
|
||||
nsPath := c.config.Namespaces.PathOf(configs.NEWNET)
|
||||
if nsPath != "" {
|
||||
// For this to work we need at least criu 3.11.0 => 31100.
|
||||
// As there was already a successful version check we will
|
||||
// not error out if it fails. runc will just behave as it used
|
||||
// to do and ignore external network namespaces.
|
||||
err := c.checkCriuVersion(31100)
|
||||
if err == nil {
|
||||
// CRIU expects the information about an external namespace
|
||||
// like this: --external net[<inode>]:<key>
|
||||
// This <key> is always 'extRootNetNS'.
|
||||
var netns syscall.Stat_t
|
||||
err = syscall.Stat(nsPath, &netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
criuExternal := fmt.Sprintf("net[%d]:extRootNetNS", netns.Ino)
|
||||
rpcOpts.External = append(rpcOpts.External, criuExternal)
|
||||
}
|
||||
}
|
||||
|
||||
fcg := c.cgroupManager.GetPaths()["freezer"]
|
||||
if fcg != "" {
|
||||
rpcOpts.FreezeCgroup = proto.String(fcg)
|
||||
|
@ -1047,7 +1059,7 @@ func (c *linuxContainer) Checkpoint(criuOpts *CriuOpts) error {
|
|||
}
|
||||
}
|
||||
|
||||
err = c.criuSwrk(nil, req, criuOpts, false)
|
||||
err = c.criuSwrk(nil, req, criuOpts, false, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1091,6 +1103,8 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
var extraFiles []*os.File
|
||||
|
||||
// TODO(avagin): Figure out how to make this work nicely. CRIU doesn't have
|
||||
// support for unprivileged restore at the moment.
|
||||
if c.config.Rootless {
|
||||
|
@ -1165,6 +1179,38 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
},
|
||||
}
|
||||
|
||||
// Same as during checkpointing. If the container has a specific network namespace
|
||||
// assigned to it, this now expects that the checkpoint will be restored in a
|
||||
// already created network namespace.
|
||||
nsPath := c.config.Namespaces.PathOf(configs.NEWNET)
|
||||
if nsPath != "" {
|
||||
// For this to work we need at least criu 3.11.0 => 31100.
|
||||
// As there was already a successful version check we will
|
||||
// not error out if it fails. runc will just behave as it used
|
||||
// to do and ignore external network namespaces.
|
||||
err := c.checkCriuVersion(31100)
|
||||
if err == nil {
|
||||
// CRIU wants the information about an existing network namespace
|
||||
// like this: --inherit-fd fd[<fd>]:<key>
|
||||
// The <key> needs to be the same as during checkpointing.
|
||||
// We are always using 'extRootNetNS' as the key in this.
|
||||
netns, err := os.Open(nsPath)
|
||||
defer netns.Close()
|
||||
if err != nil {
|
||||
logrus.Error("If a specific network namespace is defined it must exist: %s", err)
|
||||
return fmt.Errorf("Requested network namespace %v does not exist", nsPath)
|
||||
}
|
||||
inheritFd := new(criurpc.InheritFd)
|
||||
inheritFd.Key = proto.String("extRootNetNS")
|
||||
// The offset of four is necessary because 0, 1, 2 and 3 is already
|
||||
// used by stdin, stdout, stderr, 'criu swrk' socket.
|
||||
inheritFd.Fd = proto.Int32(int32(4 + len(extraFiles)))
|
||||
req.Opts.InheritFd = append(req.Opts.InheritFd, inheritFd)
|
||||
// All open FDs need to be transferred to CRIU via extraFiles
|
||||
extraFiles = append(extraFiles, netns)
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range c.config.Mounts {
|
||||
switch m.Device {
|
||||
case "bind":
|
||||
|
@ -1223,7 +1269,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
|
|||
req.Opts.InheritFd = append(req.Opts.InheritFd, inheritFd)
|
||||
}
|
||||
}
|
||||
return c.criuSwrk(process, req, criuOpts, true)
|
||||
return c.criuSwrk(process, req, criuOpts, true, extraFiles)
|
||||
}
|
||||
|
||||
func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
|
||||
|
@ -1253,7 +1299,7 @@ func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, applyCgroups bool) error {
|
||||
func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *CriuOpts, applyCgroups bool, extraFiles []*os.File) error {
|
||||
fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_SEQPACKET|unix.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1294,6 +1340,9 @@ func (c *linuxContainer) criuSwrk(process *Process, req *criurpc.CriuReq, opts *
|
|||
cmd.Stderr = process.Stderr
|
||||
}
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, criuServer)
|
||||
if extraFiles != nil {
|
||||
cmd.ExtraFiles = append(cmd.ExtraFiles, extraFiles...)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
|
@ -1801,28 +1850,22 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
|
|||
Value: []byte(c.newgidmapPath),
|
||||
})
|
||||
}
|
||||
// The following only applies if we are root.
|
||||
if !c.config.Rootless {
|
||||
// check if we have CAP_SETGID to setgroup properly
|
||||
pid, err := capability.NewPid(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !pid.Get(capability.EFFECTIVE, capability.CAP_SETGID) {
|
||||
r.AddData(&Boolmsg{
|
||||
Type: SetgroupAttr,
|
||||
Value: true,
|
||||
})
|
||||
}
|
||||
if requiresRootOrMappingTool(c.config) {
|
||||
r.AddData(&Boolmsg{
|
||||
Type: SetgroupAttr,
|
||||
Value: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write oom_score_adj
|
||||
r.AddData(&Bytemsg{
|
||||
Type: OomScoreAdjAttr,
|
||||
Value: []byte(fmt.Sprintf("%d", c.config.OomScoreAdj)),
|
||||
})
|
||||
if c.config.OomScoreAdj != nil {
|
||||
// write oom_score_adj
|
||||
r.AddData(&Bytemsg{
|
||||
Type: OomScoreAdjAttr,
|
||||
Value: []byte(fmt.Sprintf("%d", *c.config.OomScoreAdj)),
|
||||
})
|
||||
}
|
||||
|
||||
// write rootless
|
||||
r.AddData(&Boolmsg{
|
||||
|
@ -1847,3 +1890,10 @@ func ignoreTerminateErrors(err error) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func requiresRootOrMappingTool(c *configs.Config) bool {
|
||||
gidMap := []configs.IDMap{
|
||||
{ContainerID: 0, HostID: os.Getegid(), Size: 1},
|
||||
}
|
||||
return !reflect.DeepEqual(c.GidMappings, gidMap)
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/container_linux_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/container_linux_test.go
generated
vendored
|
@ -159,7 +159,7 @@ func TestGetContainerStats(t *testing.T) {
|
|||
t.Fatal("cgroup stats are nil")
|
||||
}
|
||||
if stats.CgroupStats.MemoryStats.Usage.Usage != 1024 {
|
||||
t.Fatalf("expected memory usage 1024 but recevied %d", stats.CgroupStats.MemoryStats.Usage.Usage)
|
||||
t.Fatalf("expected memory usage 1024 but received %d", stats.CgroupStats.MemoryStats.Usage.Usage)
|
||||
}
|
||||
if intelrdt.IsEnabled() {
|
||||
if stats.IntelRdtStats == nil {
|
||||
|
|
105
vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go
generated
vendored
Normal file
105
vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go
generated
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
package devices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotADevice = errors.New("not a device node")
|
||||
)
|
||||
|
||||
// Testing dependencies
|
||||
var (
|
||||
unixLstat = unix.Lstat
|
||||
ioutilReadDir = ioutil.ReadDir
|
||||
)
|
||||
|
||||
// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the information about a linux device and return that information as a Device struct.
|
||||
func DeviceFromPath(path, permissions string) (*configs.Device, error) {
|
||||
var stat unix.Stat_t
|
||||
err := unixLstat(path, &stat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
devNumber = uint64(stat.Rdev)
|
||||
major = unix.Major(devNumber)
|
||||
minor = unix.Minor(devNumber)
|
||||
)
|
||||
if major == 0 {
|
||||
return nil, ErrNotADevice
|
||||
}
|
||||
|
||||
var (
|
||||
devType rune
|
||||
mode = stat.Mode
|
||||
)
|
||||
switch {
|
||||
case mode&unix.S_IFBLK == unix.S_IFBLK:
|
||||
devType = 'b'
|
||||
case mode&unix.S_IFCHR == unix.S_IFCHR:
|
||||
devType = 'c'
|
||||
}
|
||||
return &configs.Device{
|
||||
Type: devType,
|
||||
Path: path,
|
||||
Major: int64(major),
|
||||
Minor: int64(minor),
|
||||
Permissions: permissions,
|
||||
FileMode: os.FileMode(mode),
|
||||
Uid: stat.Uid,
|
||||
Gid: stat.Gid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func HostDevices() ([]*configs.Device, error) {
|
||||
return getDevices("/dev")
|
||||
}
|
||||
|
||||
func getDevices(path string) ([]*configs.Device, error) {
|
||||
files, err := ioutilReadDir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []*configs.Device{}
|
||||
for _, f := range files {
|
||||
switch {
|
||||
case f.IsDir():
|
||||
switch f.Name() {
|
||||
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
|
||||
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
|
||||
continue
|
||||
default:
|
||||
sub, err := getDevices(filepath.Join(path, f.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out = append(out, sub...)
|
||||
continue
|
||||
}
|
||||
case f.Name() == "console":
|
||||
continue
|
||||
}
|
||||
device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
|
||||
if err != nil {
|
||||
if err == ErrNotADevice {
|
||||
continue
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, device)
|
||||
}
|
||||
return out, nil
|
||||
}
|
63
vendor/github.com/opencontainers/runc/libcontainer/devices/devices_test.go
generated
vendored
Normal file
63
vendor/github.com/opencontainers/runc/libcontainer/devices/devices_test.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package devices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestDeviceFromPathLstatFailure(t *testing.T) {
|
||||
testError := errors.New("test error")
|
||||
|
||||
// Override unix.Lstat to inject error.
|
||||
unixLstat = func(path string, stat *unix.Stat_t) error {
|
||||
return testError
|
||||
}
|
||||
|
||||
_, err := DeviceFromPath("", "")
|
||||
if err != testError {
|
||||
t.Fatalf("Unexpected error %v, expected %v", err, testError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostDevicesIoutilReadDirFailure(t *testing.T) {
|
||||
testError := errors.New("test error")
|
||||
|
||||
// Override ioutil.ReadDir to inject error.
|
||||
ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
|
||||
return nil, testError
|
||||
}
|
||||
|
||||
_, err := HostDevices()
|
||||
if err != testError {
|
||||
t.Fatalf("Unexpected error %v, expected %v", err, testError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostDevicesIoutilReadDirDeepFailure(t *testing.T) {
|
||||
testError := errors.New("test error")
|
||||
called := false
|
||||
|
||||
// Override ioutil.ReadDir to inject error after the first call.
|
||||
ioutilReadDir = func(dirname string) ([]os.FileInfo, error) {
|
||||
if called {
|
||||
return nil, testError
|
||||
}
|
||||
called = true
|
||||
|
||||
// Provoke a second call.
|
||||
fi, err := os.Lstat("/tmp")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
return []os.FileInfo{fi}, nil
|
||||
}
|
||||
|
||||
_, err := HostDevices()
|
||||
if err != testError {
|
||||
t.Fatalf("Unexpected error %v, expected %v", err, testError)
|
||||
}
|
||||
}
|
46
vendor/github.com/opencontainers/runc/libcontainer/factory_linux.go
generated
vendored
46
vendor/github.com/opencontainers/runc/libcontainer/factory_linux.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
|||
"runtime/debug"
|
||||
"strconv"
|
||||
|
||||
"github.com/cyphar/filepath-securejoin"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||
|
@ -59,9 +60,9 @@ func SystemdCgroups(l *LinuxFactory) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Cgroupfs is an options func to configure a LinuxFactory to return
|
||||
// containers that use the native cgroups filesystem implementation to
|
||||
// create and manage cgroups.
|
||||
// Cgroupfs is an options func to configure a LinuxFactory to return containers
|
||||
// that use the native cgroups filesystem implementation to create and manage
|
||||
// cgroups.
|
||||
func Cgroupfs(l *LinuxFactory) error {
|
||||
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
|
||||
return &fs.Manager{
|
||||
|
@ -72,6 +73,23 @@ func Cgroupfs(l *LinuxFactory) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RootlessCgroupfs is an options func to configure a LinuxFactory to return
|
||||
// containers that use the native cgroups filesystem implementation to create
|
||||
// and manage cgroups. The difference between RootlessCgroupfs and Cgroupfs is
|
||||
// that RootlessCgroupfs can transparently handle permission errors that occur
|
||||
// during rootless container setup (while still allowing cgroup usage if
|
||||
// they've been set up properly).
|
||||
func RootlessCgroupfs(l *LinuxFactory) error {
|
||||
l.NewCgroupsManager = func(config *configs.Cgroup, paths map[string]string) cgroups.Manager {
|
||||
return &fs.Manager{
|
||||
Cgroups: config,
|
||||
Rootless: true,
|
||||
Paths: paths,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntelRdtfs is an options func to configure a LinuxFactory to return
|
||||
// containers that use the Intel RDT "resource control" filesystem to
|
||||
// create and manage Intel Xeon platform shared resources (e.g., L3 cache).
|
||||
|
@ -178,7 +196,10 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
|
|||
if err := l.Validator.Validate(config); err != nil {
|
||||
return nil, newGenericError(err, ConfigInvalid)
|
||||
}
|
||||
containerRoot := filepath.Join(l.Root, id)
|
||||
containerRoot, err := securejoin.SecureJoin(l.Root, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := os.Stat(containerRoot); err == nil {
|
||||
return nil, newGenericError(fmt.Errorf("container with id exists: %v", id), IdInUse)
|
||||
} else if !os.IsNotExist(err) {
|
||||
|
@ -212,7 +233,14 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
|
|||
if l.Root == "" {
|
||||
return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
|
||||
}
|
||||
containerRoot := filepath.Join(l.Root, id)
|
||||
//when load, we need to check id is valid or not.
|
||||
if err := l.validateID(id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerRoot, err := securejoin.SecureJoin(l.Root, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state, err := l.loadState(containerRoot, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -322,7 +350,11 @@ func (l *LinuxFactory) StartInitialization() (err error) {
|
|||
}
|
||||
|
||||
func (l *LinuxFactory) loadState(root, id string) (*State, error) {
|
||||
f, err := os.Open(filepath.Join(root, stateFilename))
|
||||
stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(stateFilePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, newGenericError(fmt.Errorf("container %q does not exist", id), ContainerNotExists)
|
||||
|
@ -338,7 +370,7 @@ func (l *LinuxFactory) loadState(root, id string) (*State, error) {
|
|||
}
|
||||
|
||||
func (l *LinuxFactory) validateID(id string) error {
|
||||
if !idRegex.MatchString(id) {
|
||||
if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
|
||||
return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
|
||||
}
|
||||
|
||||
|
|
13
vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
generated
vendored
13
vendor/github.com/opencontainers/runc/libcontainer/init_linux.go
generated
vendored
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
@ -121,7 +122,7 @@ func finalizeNamespace(config *initConfig) error {
|
|||
// inherited are marked close-on-exec so they stay out of the
|
||||
// container
|
||||
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "close exec fds")
|
||||
}
|
||||
|
||||
capabilities := &configs.Capabilities{}
|
||||
|
@ -136,20 +137,20 @@ func finalizeNamespace(config *initConfig) error {
|
|||
}
|
||||
// drop capabilities in bounding set before changing user
|
||||
if err := w.ApplyBoundingSet(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "apply bounding set")
|
||||
}
|
||||
// preserve existing capabilities while we change users
|
||||
if err := system.SetKeepCaps(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "set keep caps")
|
||||
}
|
||||
if err := setupUser(config); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "setup user")
|
||||
}
|
||||
if err := system.ClearKeepCaps(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "clear keep caps")
|
||||
}
|
||||
if err := w.ApplyCaps(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "apply caps")
|
||||
}
|
||||
if config.Cwd != "" {
|
||||
if err := unix.Chdir(config.Cwd); err != nil {
|
||||
|
|
257
vendor/github.com/opencontainers/runc/libcontainer/integration/checkpoint_test.go
generated
vendored
Normal file
257
vendor/github.com/opencontainers/runc/libcontainer/integration/checkpoint_test.go
generated
vendored
Normal file
|
@ -0,0 +1,257 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func showFile(t *testing.T, fname string) error {
|
||||
t.Logf("=== %s ===\n", fname)
|
||||
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
t.Log(scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Logf("=== END ===\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestUsernsCheckpoint(t *testing.T) {
|
||||
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
|
||||
t.Skip("userns is unsupported")
|
||||
}
|
||||
cmd := exec.Command("criu", "check", "--feature", "userns")
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Skip("Unable to c/r a container with userns")
|
||||
}
|
||||
testCheckpoint(t, true)
|
||||
}
|
||||
|
||||
func TestCheckpoint(t *testing.T) {
|
||||
testCheckpoint(t, false)
|
||||
}
|
||||
|
||||
func testCheckpoint(t *testing.T, userns bool) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
root, err := newTestRoot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(root)
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
|
||||
config.Mounts = append(config.Mounts, &configs.Mount{
|
||||
Destination: "/sys/fs/cgroup",
|
||||
Device: "cgroup",
|
||||
Flags: defaultMountFlags | unix.MS_RDONLY,
|
||||
})
|
||||
|
||||
if userns {
|
||||
config.UidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.GidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
|
||||
}
|
||||
|
||||
factory, err := libcontainer.New(root, libcontainer.Cgroupfs)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
container, err := factory.Create("test", config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
|
||||
pconfig := libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Stdout: &stdout,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(&pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pid, err := pconfig.Pid()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentDir, err := ioutil.TempDir("", "criu-parent")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(parentDir)
|
||||
|
||||
preDumpOpts := &libcontainer.CriuOpts{
|
||||
ImagesDirectory: parentDir,
|
||||
WorkDirectory: parentDir,
|
||||
PreDump: true,
|
||||
}
|
||||
preDumpLog := filepath.Join(preDumpOpts.WorkDirectory, "dump.log")
|
||||
|
||||
if err := container.Checkpoint(preDumpOpts); err != nil {
|
||||
showFile(t, preDumpLog)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err := container.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if state != libcontainer.Running {
|
||||
t.Fatal("Unexpected preDump state: ", state)
|
||||
}
|
||||
|
||||
imagesDir, err := ioutil.TempDir("", "criu")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(imagesDir)
|
||||
|
||||
checkpointOpts := &libcontainer.CriuOpts{
|
||||
ImagesDirectory: imagesDir,
|
||||
WorkDirectory: imagesDir,
|
||||
ParentImage: "../criu-parent",
|
||||
}
|
||||
dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log")
|
||||
restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log")
|
||||
|
||||
if err := container.Checkpoint(checkpointOpts); err != nil {
|
||||
showFile(t, dumpLog)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err = container.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if state != libcontainer.Stopped {
|
||||
t.Fatal("Unexpected state checkpoint: ", state)
|
||||
}
|
||||
|
||||
stdinW.Close()
|
||||
_, err = process.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// reload the container
|
||||
container, err = factory.Load("test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoreStdinR, restoreStdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoreProcessConfig := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Stdin: restoreStdinR,
|
||||
Stdout: &stdout,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Restore(restoreProcessConfig, checkpointOpts)
|
||||
restoreStdinR.Close()
|
||||
defer restoreStdinW.Close()
|
||||
if err != nil {
|
||||
showFile(t, restoreLog)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err = container.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state != libcontainer.Running {
|
||||
t.Fatal("Unexpected restore state: ", state)
|
||||
}
|
||||
|
||||
pid, err = restoreProcessConfig.Pid()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
process, err = os.FindProcess(pid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = restoreStdinW.WriteString("Hello!")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restoreStdinW.Close()
|
||||
s, err := process.Wait()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !s.Success() {
|
||||
t.Fatal(s.String(), pid)
|
||||
}
|
||||
|
||||
output := string(stdout.Bytes())
|
||||
if !strings.Contains(output, "Hello!") {
|
||||
t.Fatal("Did not restore the pipe correctly:", output)
|
||||
}
|
||||
}
|
2
vendor/github.com/opencontainers/runc/libcontainer/integration/doc.go
generated
vendored
Normal file
2
vendor/github.com/opencontainers/runc/libcontainer/integration/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
// integration is used for integration testing of libcontainer
|
||||
package integration
|
1778
vendor/github.com/opencontainers/runc/libcontainer/integration/exec_test.go
generated
vendored
Normal file
1778
vendor/github.com/opencontainers/runc/libcontainer/integration/exec_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
608
vendor/github.com/opencontainers/runc/libcontainer/integration/execin_test.go
generated
vendored
Normal file
608
vendor/github.com/opencontainers/runc/libcontainer/integration/execin_test.go
generated
vendored
Normal file
|
@ -0,0 +1,608 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestExecIn(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"ps"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
if !strings.Contains(out, "cat") || !strings.Contains(out, "ps") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
if strings.Contains(out, "\r") {
|
||||
t.Fatalf("unexpected carriage-return in output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInUsernsRlimit(t *testing.T) {
|
||||
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
|
||||
t.Skip("userns is unsupported")
|
||||
}
|
||||
|
||||
testExecInRlimit(t, true)
|
||||
}
|
||||
|
||||
func TestExecInRlimit(t *testing.T) {
|
||||
testExecInRlimit(t, false)
|
||||
}
|
||||
|
||||
func testExecInRlimit(t *testing.T, userns bool) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
if userns {
|
||||
config.UidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.GidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"/bin/sh", "-c", "ulimit -n"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Rlimits: []configs.Rlimit{
|
||||
// increase process rlimit higher than container rlimit to test per-process limit
|
||||
{Type: unix.RLIMIT_NOFILE, Hard: 1026, Soft: 1026},
|
||||
},
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
if limit := strings.TrimSpace(out); limit != "1026" {
|
||||
t.Fatalf("expected rlimit to be 1026, got %s", limit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInAdditionalGroups(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
pconfig := libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"sh", "-c", "id", "-Gn"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
AdditionalGroups: []string{"plugdev", "audio"},
|
||||
}
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
waitProcess(&pconfig, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
outputGroups := string(stdout.Bytes())
|
||||
|
||||
// Check that the groups output has the groups that we specified
|
||||
if !strings.Contains(outputGroups, "audio") {
|
||||
t.Fatalf("Listed groups do not contain the audio group as expected: %v", outputGroups)
|
||||
}
|
||||
|
||||
if !strings.Contains(outputGroups, "plugdev") {
|
||||
t.Fatalf("Listed groups do not contain the plugdev group as expected: %v", outputGroups)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInError(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer func() {
|
||||
stdinW.Close()
|
||||
if _, err := process.Wait(); err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
}()
|
||||
ok(t, err)
|
||||
|
||||
for i := 0; i < 42; i++ {
|
||||
var out bytes.Buffer
|
||||
unexistent := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"unexistent"},
|
||||
Env: standardEnvironment,
|
||||
Stderr: &out,
|
||||
}
|
||||
err = container.Run(unexistent)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "executable file not found") {
|
||||
t.Fatalf("Should be error about not found executable, got %s", err)
|
||||
}
|
||||
if !bytes.Contains(out.Bytes(), []byte("executable file not found")) {
|
||||
t.Fatalf("executable file not found error not delivered to stdio:\n%s", out.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInTTY(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"ps"},
|
||||
Env: standardEnvironment,
|
||||
}
|
||||
parent, child, err := utils.NewSockPair("console")
|
||||
if err != nil {
|
||||
ok(t, err)
|
||||
}
|
||||
defer parent.Close()
|
||||
defer child.Close()
|
||||
ps.ConsoleSocket = child
|
||||
type cdata struct {
|
||||
c console.Console
|
||||
err error
|
||||
}
|
||||
dc := make(chan *cdata, 1)
|
||||
go func() {
|
||||
f, err := utils.RecvFd(parent)
|
||||
if err != nil {
|
||||
dc <- &cdata{
|
||||
err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
c, err := console.ConsoleFromFile(f)
|
||||
if err != nil {
|
||||
dc <- &cdata{
|
||||
err: err,
|
||||
}
|
||||
return
|
||||
}
|
||||
console.ClearONLCR(c.Fd())
|
||||
dc <- &cdata{
|
||||
c: c,
|
||||
}
|
||||
}()
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
data := <-dc
|
||||
if data.err != nil {
|
||||
ok(t, data.err)
|
||||
}
|
||||
console := data.c
|
||||
copy := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(&stdout, console)
|
||||
close(copy)
|
||||
}()
|
||||
ok(t, err)
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Waiting for copy timed out")
|
||||
case <-copy:
|
||||
}
|
||||
waitProcess(ps, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := stdout.String()
|
||||
if !strings.Contains(out, "cat") || !strings.Contains(out, "ps") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
if strings.Contains(out, "\r") {
|
||||
t.Fatalf("unexpected carriage-return in output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInEnvironment(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
process2 := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"env"},
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"DEBUG=true",
|
||||
"DEBUG=false",
|
||||
"ENV=test",
|
||||
},
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process2)
|
||||
ok(t, err)
|
||||
waitProcess(process2, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
// check execin's process environment
|
||||
if !strings.Contains(out, "DEBUG=false") ||
|
||||
!strings.Contains(out, "ENV=test") ||
|
||||
!strings.Contains(out, "HOME=/root") ||
|
||||
!strings.Contains(out, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin") ||
|
||||
strings.Contains(out, "DEBUG=true") {
|
||||
t.Fatalf("unexpected running process, output %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecinPassExtraFiles(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
pipeout1, pipein1, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pipeout2, pipein2, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
inprocess := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"},
|
||||
Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
||||
ExtraFiles: []*os.File{pipein1, pipein2},
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Run(inprocess)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
waitProcess(inprocess, t)
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := string(stdout.Bytes())
|
||||
// fd 5 is the directory handle for /proc/$$/fd
|
||||
if out != "0 1 2 3 4 5" {
|
||||
t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to exec, got '%s'", out)
|
||||
}
|
||||
var buf = []byte{0}
|
||||
_, err = pipeout1.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out1 := string(buf)
|
||||
if out1 != "1" {
|
||||
t.Fatalf("expected first pipe to receive '1', got '%s'", out1)
|
||||
}
|
||||
|
||||
_, err = pipeout2.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out2 := string(buf)
|
||||
if out2 != "2" {
|
||||
t.Fatalf("expected second pipe to receive '2', got '%s'", out2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInOomScoreAdj(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.OomScoreAdj = ptrInt(200)
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
ps := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"/bin/sh", "-c", "cat /proc/self/oom_score_adj"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
out := buffers.Stdout.String()
|
||||
if oomScoreAdj := strings.TrimSpace(out); oomScoreAdj != strconv.Itoa(*config.OomScoreAdj) {
|
||||
t.Fatalf("expected oomScoreAdj to be %d, got %s", *config.OomScoreAdj, oomScoreAdj)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecInUserns(t *testing.T) {
|
||||
if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
|
||||
t.Skip("userns is unsupported")
|
||||
}
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
rootfs, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs)
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.UidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.GidMappings = []configs.IDMap{{HostID: 0, ContainerID: 0, Size: 1000}}
|
||||
config.Namespaces = append(config.Namespaces, configs.Namespace{Type: configs.NEWUSER})
|
||||
container, err := newContainer(config)
|
||||
ok(t, err)
|
||||
defer container.Destroy()
|
||||
|
||||
// Execute a first process in the container
|
||||
stdinR, stdinW, err := os.Pipe()
|
||||
ok(t, err)
|
||||
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"cat"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
Init: true,
|
||||
}
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
|
||||
initPID, err := process.Pid()
|
||||
ok(t, err)
|
||||
initUserns, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/user", initPID))
|
||||
ok(t, err)
|
||||
|
||||
buffers := newStdBuffers()
|
||||
process2 := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"readlink", "/proc/self/ns/user"},
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
},
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
err = container.Run(process2)
|
||||
ok(t, err)
|
||||
waitProcess(process2, t)
|
||||
stdinW.Close()
|
||||
waitProcess(process, t)
|
||||
|
||||
if out := strings.TrimSpace(buffers.Stdout.String()); out != initUserns {
|
||||
t.Errorf("execin userns(%s), wanted %s", out, initUserns)
|
||||
}
|
||||
}
|
61
vendor/github.com/opencontainers/runc/libcontainer/integration/init_test.go
generated
vendored
Normal file
61
vendor/github.com/opencontainers/runc/libcontainer/integration/init_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// init runs the libcontainer initialization code because of the busybox style needs
|
||||
// to work around the go runtime and the issues with forking
|
||||
func init() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "init" {
|
||||
return
|
||||
}
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
factory, err := libcontainer.New("")
|
||||
if err != nil {
|
||||
logrus.Fatalf("unable to initialize for container: %s", err)
|
||||
}
|
||||
if err := factory.StartInitialization(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
factory libcontainer.Factory
|
||||
systemdFactory libcontainer.Factory
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var (
|
||||
err error
|
||||
ret int
|
||||
)
|
||||
|
||||
logrus.SetOutput(os.Stderr)
|
||||
logrus.SetLevel(logrus.InfoLevel)
|
||||
|
||||
factory, err = libcontainer.New("/run/libctTests", libcontainer.Cgroupfs)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if systemd.UseSystemd() {
|
||||
systemdFactory, err = libcontainer.New("/run/libctTests", libcontainer.SystemdCgroups)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
ret = m.Run()
|
||||
os.Exit(ret)
|
||||
}
|
422
vendor/github.com/opencontainers/runc/libcontainer/integration/seccomp_test.go
generated
vendored
Normal file
422
vendor/github.com/opencontainers/runc/libcontainer/integration/seccomp_test.go
generated
vendored
Normal file
|
@ -0,0 +1,422 @@
|
|||
// +build linux,cgo,seccomp
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
||||
)
|
||||
|
||||
func TestSeccompDenyGetcwd(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "getcwd",
|
||||
Action: configs.Errno,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
buffers := newStdBuffers()
|
||||
pwd := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"pwd"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(pwd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ps, err := pwd.Wait()
|
||||
if err == nil {
|
||||
t.Fatal("Expecting error (negative return code); instead exited cleanly!")
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
} else if status.Signaled() {
|
||||
exitCode = -int(status.Signal())
|
||||
} else {
|
||||
t.Fatalf("Unrecognized exit reason!")
|
||||
}
|
||||
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Getcwd should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
|
||||
expected := "pwd: getcwd: Operation not permitted"
|
||||
actual := strings.Trim(buffers.Stderr.String(), "\n")
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected output %s but got %s\n", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompPermitWriteConditional(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
buffers := newStdBuffers()
|
||||
dmesg := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"busybox", "ls", "/"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(dmesg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := dmesg.Wait(); err != nil {
|
||||
t.Fatalf("%s: %s", err, buffers.Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompDenyWriteConditional(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
// Only test if library version is v2.2.1 or higher
|
||||
// Conditional filtering will always error in v2.2.0 and lower
|
||||
major, minor, micro := libseccomp.GetLibraryVersion()
|
||||
if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer container.Destroy()
|
||||
|
||||
buffers := newStdBuffers()
|
||||
dmesg := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: []string{"busybox", "ls", "does_not_exist"},
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(dmesg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ps, err := dmesg.Wait()
|
||||
if err == nil {
|
||||
t.Fatal("Expecting negative return, instead got 0!")
|
||||
}
|
||||
|
||||
var exitCode int
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
} else if status.Signaled() {
|
||||
exitCode = -int(status.Signal())
|
||||
} else {
|
||||
t.Fatalf("Unrecognized exit reason!")
|
||||
}
|
||||
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
|
||||
// We're denying write to stderr, so we expect an empty buffer
|
||||
expected := ""
|
||||
actual := strings.Trim(buffers.Stderr.String(), "\n")
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected output %s but got %s\n", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompPermitWriteMultipleConditions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Value: 0,
|
||||
Op: configs.NotEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/")
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", buffers, err)
|
||||
}
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
|
||||
}
|
||||
// We don't need to verify the actual thing printed
|
||||
// Just that something was written to stdout
|
||||
if len(buffers.Stdout.String()) == 0 {
|
||||
t.Fatalf("Nothing was written to stdout, write call failed!\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompDenyWriteMultipleConditions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
// Only test if library version is v2.2.1 or higher
|
||||
// Conditional filtering will always error in v2.2.0 and lower
|
||||
major, minor, micro := libseccomp.GetLibraryVersion()
|
||||
if (major == 2 && minor < 2) || (major == 2 && minor == 2 && micro < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 2,
|
||||
Value: 0,
|
||||
Op: configs.NotEqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
|
||||
if err == nil {
|
||||
t.Fatalf("Expecting error return, instead got 0")
|
||||
}
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
|
||||
expected := ""
|
||||
actual := strings.Trim(buffers.Stderr.String(), "\n")
|
||||
if actual != expected {
|
||||
t.Fatalf("Expected output %s but got %s\n", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompMultipleConditionSameArgDeniesStdout(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
// Prevent writing to both stdout and stderr
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 1,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/")
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %s", buffers, err)
|
||||
}
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
|
||||
}
|
||||
// Verify that nothing was printed
|
||||
if len(buffers.Stdout.String()) != 0 {
|
||||
t.Fatalf("Something was written to stdout, write call succeeded!\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeccompMultipleConditionSameArgDeniesStderr(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
rootfs, err := newRootfs()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer remove(rootfs)
|
||||
|
||||
// Prevent writing to both stdout and stderr
|
||||
config := newTemplateConfig(rootfs)
|
||||
config.Seccomp = &configs.Seccomp{
|
||||
DefaultAction: configs.Allow,
|
||||
Syscalls: []*configs.Syscall{
|
||||
{
|
||||
Name: "write",
|
||||
Action: configs.Errno,
|
||||
Args: []*configs.Arg{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 1,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
{
|
||||
Index: 0,
|
||||
Value: 2,
|
||||
Op: configs.EqualTo,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
|
||||
if err == nil {
|
||||
t.Fatalf("Expecting error return, instead got 0")
|
||||
}
|
||||
if exitCode == 0 {
|
||||
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
|
||||
}
|
||||
// Verify nothing was printed
|
||||
if len(buffers.Stderr.String()) != 0 {
|
||||
t.Fatalf("Something was written to stderr, write call succeeded!\n")
|
||||
}
|
||||
}
|
191
vendor/github.com/opencontainers/runc/libcontainer/integration/template_test.go
generated
vendored
Normal file
191
vendor/github.com/opencontainers/runc/libcontainer/integration/template_test.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var standardEnvironment = []string{
|
||||
"HOME=/root",
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"HOSTNAME=integration",
|
||||
"TERM=xterm",
|
||||
}
|
||||
|
||||
const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
|
||||
|
||||
// newTemplateConfig returns a base template for running a container
|
||||
//
|
||||
// it uses a network strategy of just setting a loopback interface
|
||||
// and the default setup for devices
|
||||
func newTemplateConfig(rootfs string) *configs.Config {
|
||||
allowAllDevices := false
|
||||
return &configs.Config{
|
||||
Rootfs: rootfs,
|
||||
Capabilities: &configs.Capabilities{
|
||||
Bounding: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Permitted: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Inheritable: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Ambient: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
Effective: []string{
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE",
|
||||
},
|
||||
},
|
||||
Namespaces: configs.Namespaces([]configs.Namespace{
|
||||
{Type: configs.NEWNS},
|
||||
{Type: configs.NEWUTS},
|
||||
{Type: configs.NEWIPC},
|
||||
{Type: configs.NEWPID},
|
||||
{Type: configs.NEWNET},
|
||||
}),
|
||||
Cgroups: &configs.Cgroup{
|
||||
Path: "integration/test",
|
||||
Resources: &configs.Resources{
|
||||
MemorySwappiness: nil,
|
||||
AllowAllDevices: &allowAllDevices,
|
||||
AllowedDevices: configs.DefaultAllowedDevices,
|
||||
},
|
||||
},
|
||||
MaskPaths: []string{
|
||||
"/proc/kcore",
|
||||
"/sys/firmware",
|
||||
},
|
||||
ReadonlyPaths: []string{
|
||||
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||
},
|
||||
Devices: configs.DefaultAutoCreatedDevices,
|
||||
Hostname: "integration",
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Source: "proc",
|
||||
Destination: "/proc",
|
||||
Device: "proc",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
{
|
||||
Source: "tmpfs",
|
||||
Destination: "/dev",
|
||||
Device: "tmpfs",
|
||||
Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
|
||||
Data: "mode=755",
|
||||
},
|
||||
{
|
||||
Source: "devpts",
|
||||
Destination: "/dev/pts",
|
||||
Device: "devpts",
|
||||
Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
|
||||
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||
},
|
||||
{
|
||||
Device: "tmpfs",
|
||||
Source: "shm",
|
||||
Destination: "/dev/shm",
|
||||
Data: "mode=1777,size=65536k",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
/*
|
||||
CI is broken on the debian based kernels with this
|
||||
{
|
||||
Source: "mqueue",
|
||||
Destination: "/dev/mqueue",
|
||||
Device: "mqueue",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
*/
|
||||
{
|
||||
Source: "sysfs",
|
||||
Destination: "/sys",
|
||||
Device: "sysfs",
|
||||
Flags: defaultMountFlags | unix.MS_RDONLY,
|
||||
},
|
||||
},
|
||||
Networks: []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
Address: "127.0.0.1/0",
|
||||
Gateway: "localhost",
|
||||
},
|
||||
},
|
||||
Rlimits: []configs.Rlimit{
|
||||
{
|
||||
Type: unix.RLIMIT_NOFILE,
|
||||
Hard: uint64(1025),
|
||||
Soft: uint64(1025),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
175
vendor/github.com/opencontainers/runc/libcontainer/integration/utils_test.go
generated
vendored
Normal file
175
vendor/github.com/opencontainers/runc/libcontainer/integration/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
|
||||
func ptrInt(v int) *int {
|
||||
return &v
|
||||
}
|
||||
|
||||
func newStdBuffers() *stdBuffers {
|
||||
return &stdBuffers{
|
||||
Stdin: bytes.NewBuffer(nil),
|
||||
Stdout: bytes.NewBuffer(nil),
|
||||
Stderr: bytes.NewBuffer(nil),
|
||||
}
|
||||
}
|
||||
|
||||
type stdBuffers struct {
|
||||
Stdin *bytes.Buffer
|
||||
Stdout *bytes.Buffer
|
||||
Stderr *bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *stdBuffers) String() string {
|
||||
s := []string{}
|
||||
if b.Stderr != nil {
|
||||
s = append(s, b.Stderr.String())
|
||||
}
|
||||
if b.Stdout != nil {
|
||||
s = append(s, b.Stdout.String())
|
||||
}
|
||||
return strings.Join(s, "|")
|
||||
}
|
||||
|
||||
// ok fails the test if an err is not nil.
|
||||
func ok(t testing.TB, err error) {
|
||||
if err != nil {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func waitProcess(p *libcontainer.Process, t *testing.T) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
status, err := p.Wait()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("%s:%d: unexpected error: %s\n\n", filepath.Base(file), line, err.Error())
|
||||
}
|
||||
|
||||
if !status.Success() {
|
||||
t.Fatalf("%s:%d: unexpected status: %s\n\n", filepath.Base(file), line, status.String())
|
||||
}
|
||||
}
|
||||
|
||||
func newTestRoot() (string, error) {
|
||||
dir, err := ioutil.TempDir("", "libcontainer")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func newTestBundle() (string, error) {
|
||||
dir, err := ioutil.TempDir("", "bundle")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// newRootfs creates a new tmp directory and copies the busybox root filesystem
|
||||
func newRootfs() (string, error) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := copyBusybox(dir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func remove(dir string) {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// copyBusybox copies the rootfs for a busybox container created for the test image
|
||||
// into the new directory for the specific test
|
||||
func copyBusybox(dest string) error {
|
||||
out, err := exec.Command("sh", "-c", fmt.Sprintf("cp -a /busybox/* %s/", dest)).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy error %q: %q", err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newContainer(config *configs.Config) (libcontainer.Container, error) {
|
||||
h := md5.New()
|
||||
h.Write([]byte(time.Now().String()))
|
||||
return newContainerWithName(hex.EncodeToString(h.Sum(nil)), config)
|
||||
}
|
||||
|
||||
func newContainerWithName(name string, config *configs.Config) (libcontainer.Container, error) {
|
||||
f := factory
|
||||
if config.Cgroups != nil && config.Cgroups.Parent == "system.slice" {
|
||||
f = systemdFactory
|
||||
}
|
||||
return f.Create(name, config)
|
||||
}
|
||||
|
||||
// runContainer runs the container with the specific config and arguments
|
||||
//
|
||||
// buffers are returned containing the STDOUT and STDERR output for the run
|
||||
// along with the exit code and any go error
|
||||
func runContainer(config *configs.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) {
|
||||
container, err := newContainer(config)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
defer container.Destroy()
|
||||
buffers = newStdBuffers()
|
||||
process := &libcontainer.Process{
|
||||
Cwd: "/",
|
||||
Args: args,
|
||||
Env: standardEnvironment,
|
||||
Stdin: buffers.Stdin,
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
Init: true,
|
||||
}
|
||||
|
||||
err = container.Run(process)
|
||||
if err != nil {
|
||||
return buffers, -1, err
|
||||
}
|
||||
ps, err := process.Wait()
|
||||
if err != nil {
|
||||
return buffers, -1, err
|
||||
}
|
||||
status := ps.Sys().(syscall.WaitStatus)
|
||||
if status.Exited() {
|
||||
exitCode = status.ExitStatus()
|
||||
} else if status.Signaled() {
|
||||
exitCode = -int(status.Signal())
|
||||
} else {
|
||||
return buffers, -1, err
|
||||
}
|
||||
return
|
||||
}
|
2
vendor/github.com/opencontainers/runc/libcontainer/intelrdt/intelrdt.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/intelrdt/intelrdt.go
generated
vendored
|
@ -177,7 +177,7 @@ func findIntelRdtMountpointDir() (string, error) {
|
|||
}
|
||||
|
||||
if postSeparatorFields[0] == "resctrl" {
|
||||
// Check that the mount is properly formated.
|
||||
// Check that the mount is properly formatted.
|
||||
if numPostFields < 3 {
|
||||
return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text)
|
||||
}
|
||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/message_linux.go
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/message_linux.go
generated
vendored
|
@ -77,13 +77,13 @@ func (msg *Boolmsg) Serialize() []byte {
|
|||
native.PutUint16(buf[0:2], uint16(msg.Len()))
|
||||
native.PutUint16(buf[2:4], msg.Type)
|
||||
if msg.Value {
|
||||
buf[4] = 1
|
||||
native.PutUint32(buf[4:8], uint32(1))
|
||||
} else {
|
||||
buf[4] = 0
|
||||
native.PutUint32(buf[4:8], uint32(0))
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (msg *Boolmsg) Len() int {
|
||||
return unix.NLA_HDRLEN + 1
|
||||
return unix.NLA_HDRLEN + 4 // alignment
|
||||
}
|
||||
|
|
157
vendor/github.com/opencontainers/runc/libcontainer/network_linux.go
generated
vendored
157
vendor/github.com/opencontainers/runc/libcontainer/network_linux.go
generated
vendored
|
@ -5,18 +5,15 @@ package libcontainer
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
var strategies = map[string]networkStrategy{
|
||||
"veth": &veth{},
|
||||
"loopback": &loopback{},
|
||||
}
|
||||
|
||||
|
@ -103,157 +100,3 @@ func (l *loopback) attach(n *configs.Network) (err error) {
|
|||
func (l *loopback) detach(n *configs.Network) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// veth is a network strategy that uses a bridge and creates
|
||||
// a veth pair, one that is attached to the bridge on the host and the other
|
||||
// is placed inside the container's namespace
|
||||
type veth struct {
|
||||
}
|
||||
|
||||
func (v *veth) detach(n *configs.Network) (err error) {
|
||||
return netlink.LinkSetMaster(&netlink.Device{LinkAttrs: netlink.LinkAttrs{Name: n.HostInterfaceName}}, nil)
|
||||
}
|
||||
|
||||
// attach a container network interface to an external network
|
||||
func (v *veth) attach(n *configs.Network) (err error) {
|
||||
brl, err := netlink.LinkByName(n.Bridge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
br, ok := brl.(*netlink.Bridge)
|
||||
if !ok {
|
||||
return fmt.Errorf("Wrong device type %T", brl)
|
||||
}
|
||||
host, err := netlink.LinkByName(n.HostInterfaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetMaster(host, br); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetMTU(host, n.Mtu); err != nil {
|
||||
return err
|
||||
}
|
||||
if n.HairpinMode {
|
||||
if err := netlink.LinkSetHairpin(host, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := netlink.LinkSetUp(host); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *veth) create(n *network, nspid int) (err error) {
|
||||
tmpName, err := v.generateTempPeerName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.TempVethPeerName = tmpName
|
||||
if n.Bridge == "" {
|
||||
return fmt.Errorf("bridge is not specified")
|
||||
}
|
||||
veth := &netlink.Veth{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: n.HostInterfaceName,
|
||||
TxQLen: n.TxQueueLen,
|
||||
},
|
||||
PeerName: n.TempVethPeerName,
|
||||
}
|
||||
if err := netlink.LinkAdd(veth); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
netlink.LinkDel(veth)
|
||||
}
|
||||
}()
|
||||
if err := v.attach(&n.Network); err != nil {
|
||||
return err
|
||||
}
|
||||
child, err := netlink.LinkByName(n.TempVethPeerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkSetNsPid(child, nspid)
|
||||
}
|
||||
|
||||
func (v *veth) generateTempPeerName() (string, error) {
|
||||
return utils.GenerateRandomName("veth", 7)
|
||||
}
|
||||
|
||||
func (v *veth) initialize(config *network) error {
|
||||
peer := config.TempVethPeerName
|
||||
if peer == "" {
|
||||
return fmt.Errorf("peer is not specified")
|
||||
}
|
||||
child, err := netlink.LinkByName(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetDown(child); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetName(child, config.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
// get the interface again after we changed the name as the index also changes.
|
||||
if child, err = netlink.LinkByName(config.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.MacAddress != "" {
|
||||
mac, err := net.ParseMAC(config.MacAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetHardwareAddr(child, mac); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ip, err := netlink.ParseAddr(config.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(child, ip); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.IPv6Address != "" {
|
||||
ip6, err := netlink.ParseAddr(config.IPv6Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.AddrAdd(child, ip6); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := netlink.LinkSetMTU(child, config.Mtu); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := netlink.LinkSetUp(child); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Gateway != "" {
|
||||
gw := net.ParseIP(config.Gateway)
|
||||
if err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: child.Attrs().Index,
|
||||
Gw: gw,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if config.IPv6Gateway != "" {
|
||||
gw := net.ParseIP(config.IPv6Gateway)
|
||||
if err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: child.Attrs().Index,
|
||||
Gw: gw,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
6
vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md
generated
vendored
6
vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md
generated
vendored
|
@ -10,8 +10,8 @@ The `nsenter` package will `import "C"` and it uses [cgo](https://golang.org/cmd
|
|||
package. In cgo, if the import of "C" is immediately preceded by a comment, that comment,
|
||||
called the preamble, is used as a header when compiling the C parts of the package.
|
||||
So every time we import package `nsenter`, the C code function `nsexec()` would be
|
||||
called. And package `nsenter` is now only imported in `main_unix.go`, so every time
|
||||
before we call `cmd.Start` on linux, that C code would run.
|
||||
called. And package `nsenter` is only imported in `init.go`, so every time the runc
|
||||
`init` command is invoked, that C code is run.
|
||||
|
||||
Because `nsexec()` must be run before the Go runtime in order to use the
|
||||
Linux kernel namespace, you must `import` this library into a package if
|
||||
|
@ -37,7 +37,7 @@ the parent `nsexec()` will exit and the child `nsexec()` process will
|
|||
return to allow the Go runtime take over.
|
||||
|
||||
NOTE: We do both `setns(2)` and `clone(2)` even if we don't have any
|
||||
CLONE_NEW* clone flags because we must fork a new process in order to
|
||||
`CLONE_NEW*` clone flags because we must fork a new process in order to
|
||||
enter the PID namespace.
|
||||
|
||||
|
||||
|
|
65
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
generated
vendored
65
vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c
generated
vendored
|
@ -211,7 +211,7 @@ static int try_mapping_tool(const char *app, int pid, char *map, size_t map_len)
|
|||
|
||||
/*
|
||||
* If @app is NULL, execve will segfault. Just check it here and bail (if
|
||||
* we're in this path, the caller is already getting desparate and there
|
||||
* we're in this path, the caller is already getting desperate and there
|
||||
* isn't a backup to this failing). This usually would be a configuration
|
||||
* or programming issue.
|
||||
*/
|
||||
|
@ -505,7 +505,8 @@ void join_namespaces(char *nslist)
|
|||
|
||||
ns->fd = fd;
|
||||
ns->ns = nsflag(namespace);
|
||||
strncpy(ns->path, path, PATH_MAX);
|
||||
strncpy(ns->path, path, PATH_MAX - 1);
|
||||
ns->path[PATH_MAX - 1] = '\0';
|
||||
} while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL);
|
||||
|
||||
/*
|
||||
|
@ -678,17 +679,15 @@ void nsexec(void)
|
|||
/*
|
||||
* Enable setgroups(2) if we've been asked to. But we also
|
||||
* have to explicitly disable setgroups(2) if we're
|
||||
* creating a rootless container (this is required since
|
||||
* Linux 3.19).
|
||||
* creating a rootless container for single-entry mapping.
|
||||
* i.e. config.is_setgroup == false.
|
||||
* (this is required since Linux 3.19).
|
||||
*
|
||||
* For rootless multi-entry mapping, config.is_setgroup shall be true and
|
||||
* newuidmap/newgidmap shall be used.
|
||||
*/
|
||||
if (config.is_rootless && config.is_setgroup) {
|
||||
kill(child, SIGKILL);
|
||||
bail("cannot allow setgroup in an unprivileged user namespace setup");
|
||||
}
|
||||
|
||||
if (config.is_setgroup)
|
||||
update_setgroups(child, SETGROUPS_ALLOW);
|
||||
if (config.is_rootless)
|
||||
if (config.is_rootless && !config.is_setgroup)
|
||||
update_setgroups(child, SETGROUPS_DENY);
|
||||
|
||||
/* Set up mappings. */
|
||||
|
@ -809,25 +808,30 @@ void nsexec(void)
|
|||
if (config.namespaces)
|
||||
join_namespaces(config.namespaces);
|
||||
|
||||
/*
|
||||
* Unshare all of the namespaces. Now, it should be noted that this
|
||||
* ordering might break in the future (especially with rootless
|
||||
* containers). But for now, it's not possible to split this into
|
||||
* CLONE_NEWUSER + [the rest] because of some RHEL SELinux issues.
|
||||
*
|
||||
* Note that we don't merge this with clone() because there were
|
||||
* some old kernel versions where clone(CLONE_PARENT | CLONE_NEWPID)
|
||||
* was broken, so we'll just do it the long way anyway.
|
||||
*/
|
||||
if (unshare(config.cloneflags) < 0)
|
||||
bail("failed to unshare namespaces");
|
||||
|
||||
/*
|
||||
* Deal with user namespaces first. They are quite special, as they
|
||||
* affect our ability to unshare other namespaces and are used as
|
||||
* context for privilege checks.
|
||||
*
|
||||
* We don't unshare all namespaces in one go. The reason for this
|
||||
* is that, while the kernel documentation may claim otherwise,
|
||||
* there are certain cases where unsharing all namespaces at once
|
||||
* will result in namespace objects being owned incorrectly.
|
||||
* Ideally we should just fix these kernel bugs, but it's better to
|
||||
* be safe than sorry, and fix them separately.
|
||||
*
|
||||
* A specific case of this is that the SELinux label of the
|
||||
* internal kern-mount that mqueue uses will be incorrect if the
|
||||
* UTS namespace is cloned before the USER namespace is mapped.
|
||||
* I've also heard of similar problems with the network namespace
|
||||
* in some scenarios. This also mirrors how LXC deals with this
|
||||
* problem.
|
||||
*/
|
||||
if (config.cloneflags & CLONE_NEWUSER) {
|
||||
if (unshare(CLONE_NEWUSER) < 0)
|
||||
bail("failed to unshare user namespace");
|
||||
config.cloneflags &= ~CLONE_NEWUSER;
|
||||
|
||||
/*
|
||||
* We don't have the privileges to do any mapping here (see the
|
||||
* clone_parent rant). So signal our parent to hook us up.
|
||||
|
@ -853,8 +857,21 @@ void nsexec(void)
|
|||
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0)
|
||||
bail("failed to set process as dumpable");
|
||||
}
|
||||
|
||||
/* Become root in the namespace proper. */
|
||||
if (setresuid(0, 0, 0) < 0)
|
||||
bail("failed to become root in user namespace");
|
||||
}
|
||||
|
||||
/*
|
||||
* Unshare all of the namespaces. Note that we don't merge this
|
||||
* with clone() because there were some old kernel versions where
|
||||
* clone(CLONE_PARENT | CLONE_NEWPID) was broken, so we'll just do
|
||||
* it the long way.
|
||||
*/
|
||||
if (unshare(config.cloneflags) < 0)
|
||||
bail("failed to unshare namespaces");
|
||||
|
||||
/*
|
||||
* TODO: What about non-namespace clone flags that we're dropping here?
|
||||
*
|
||||
|
|
3
vendor/github.com/opencontainers/runc/libcontainer/process.go
generated
vendored
3
vendor/github.com/opencontainers/runc/libcontainer/process.go
generated
vendored
|
@ -72,6 +72,9 @@ type Process struct {
|
|||
// ConsoleSocket provides the masterfd console.
|
||||
ConsoleSocket *os.File
|
||||
|
||||
// Init specifies whether the process is the first process in the container.
|
||||
Init bool
|
||||
|
||||
ops processOperations
|
||||
}
|
||||
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/process_linux.go
generated
vendored
|
@ -537,7 +537,7 @@ func (p *Process) InitializeIO(rootuid, rootgid int) (i *IO, err error) {
|
|||
}
|
||||
fds = append(fds, r.Fd(), w.Fd())
|
||||
p.Stderr, i.Stderr = w, r
|
||||
// change ownership of the pipes incase we are in a user namespace
|
||||
// change ownership of the pipes in case we are in a user namespace
|
||||
for _, fd := range fds {
|
||||
if err := unix.Fchown(int(fd), rootuid, rootgid); err != nil {
|
||||
return nil, err
|
||||
|
|
30
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
generated
vendored
30
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux.go
generated
vendored
|
@ -152,6 +152,26 @@ func finalizeRootfs(config *configs.Config) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// /tmp has to be mounted as private to allow MS_MOVE to work in all situations
|
||||
func prepareTmp(topTmpDir string) (string, error) {
|
||||
tmpdir, err := ioutil.TempDir(topTmpDir, "runctop")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := unix.Mount(tmpdir, tmpdir, "bind", unix.MS_BIND, ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := unix.Mount("", tmpdir, "", uintptr(unix.MS_PRIVATE), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpdir, nil
|
||||
}
|
||||
|
||||
func cleanupTmp(tmpdir string) error {
|
||||
unix.Unmount(tmpdir, 0)
|
||||
return os.RemoveAll(tmpdir)
|
||||
}
|
||||
|
||||
func mountCmd(cmd configs.Command) error {
|
||||
command := exec.Command(cmd.Path, cmd.Args[:]...)
|
||||
command.Env = cmd.Env
|
||||
|
@ -199,7 +219,12 @@ func mountToRootfs(m *configs.Mount, rootfs, mountLabel string) error {
|
|||
}
|
||||
}
|
||||
if copyUp {
|
||||
tmpDir, err = ioutil.TempDir("/tmp", "runctmpdir")
|
||||
tmpdir, err := prepareTmp("/tmp")
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "tmpcopyup: failed to setup tmpdir")
|
||||
}
|
||||
defer cleanupTmp(tmpdir)
|
||||
tmpDir, err = ioutil.TempDir(tmpdir, "runctmpdir")
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "tmpcopyup: failed to create tmpdir")
|
||||
}
|
||||
|
@ -396,6 +421,7 @@ func checkMountDestination(rootfs, dest string) error {
|
|||
"/proc/stat",
|
||||
"/proc/swaps",
|
||||
"/proc/uptime",
|
||||
"/proc/loadavg",
|
||||
"/proc/net/dev",
|
||||
}
|
||||
for _, valid := range validDestinations {
|
||||
|
@ -412,7 +438,7 @@ func checkMountDestination(rootfs, dest string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "." || !strings.HasPrefix(path, "..") {
|
||||
if path != "." && !strings.HasPrefix(path, "..") {
|
||||
return fmt.Errorf("%q cannot be mounted because it is located inside %q", dest, invalid)
|
||||
}
|
||||
}
|
||||
|
|
10
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux_test.go
generated
vendored
10
vendor/github.com/opencontainers/runc/libcontainer/rootfs_linux_test.go
generated
vendored
|
@ -9,13 +9,21 @@ import (
|
|||
)
|
||||
|
||||
func TestCheckMountDestOnProc(t *testing.T) {
|
||||
dest := "/rootfs/proc/"
|
||||
dest := "/rootfs/proc/sys"
|
||||
err := checkMountDestination("/rootfs", dest)
|
||||
if err == nil {
|
||||
t.Fatal("destination inside proc should return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMountDestOnProcChroot(t *testing.T) {
|
||||
dest := "/rootfs/proc/"
|
||||
err := checkMountDestination("/rootfs", dest)
|
||||
if err != nil {
|
||||
t.Fatal("destination inside proc when using chroot should not return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMountDestInSys(t *testing.T) {
|
||||
dest := "/rootfs//sys/fs/cgroup"
|
||||
err := checkMountDestination("/rootfs", dest)
|
||||
|
|
47
vendor/github.com/opencontainers/runc/libcontainer/seccomp/fixtures/proc_self_status
generated
vendored
Normal file
47
vendor/github.com/opencontainers/runc/libcontainer/seccomp/fixtures/proc_self_status
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
Name: cat
|
||||
State: R (running)
|
||||
Tgid: 19383
|
||||
Ngid: 0
|
||||
Pid: 19383
|
||||
PPid: 19275
|
||||
TracerPid: 0
|
||||
Uid: 1000 1000 1000 1000
|
||||
Gid: 1000 1000 1000 1000
|
||||
FDSize: 256
|
||||
Groups: 24 25 27 29 30 44 46 102 104 108 111 1000 1001
|
||||
NStgid: 19383
|
||||
NSpid: 19383
|
||||
NSpgid: 19383
|
||||
NSsid: 19275
|
||||
VmPeak: 5944 kB
|
||||
VmSize: 5944 kB
|
||||
VmLck: 0 kB
|
||||
VmPin: 0 kB
|
||||
VmHWM: 744 kB
|
||||
VmRSS: 744 kB
|
||||
VmData: 324 kB
|
||||
VmStk: 136 kB
|
||||
VmExe: 48 kB
|
||||
VmLib: 1776 kB
|
||||
VmPTE: 32 kB
|
||||
VmPMD: 12 kB
|
||||
VmSwap: 0 kB
|
||||
Threads: 1
|
||||
SigQ: 0/30067
|
||||
SigPnd: 0000000000000000
|
||||
ShdPnd: 0000000000000000
|
||||
SigBlk: 0000000000000000
|
||||
SigIgn: 0000000000000080
|
||||
SigCgt: 0000000000000000
|
||||
CapInh: 0000000000000000
|
||||
CapPrm: 0000000000000000
|
||||
CapEff: 0000000000000000
|
||||
CapBnd: 0000003fffffffff
|
||||
CapAmb: 0000000000000000
|
||||
Seccomp: 0
|
||||
Cpus_allowed: f
|
||||
Cpus_allowed_list: 0-3
|
||||
Mems_allowed: 00000000,00000001
|
||||
Mems_allowed_list: 0
|
||||
voluntary_ctxt_switches: 0
|
||||
nonvoluntary_ctxt_switches: 1
|
18
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux.go
generated
vendored
18
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux.go
generated
vendored
|
@ -216,7 +216,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
}
|
||||
config.Namespaces.Add(t, ns.Path)
|
||||
}
|
||||
if config.Namespaces.Contains(configs.NEWNET) {
|
||||
if config.Namespaces.Contains(configs.NEWNET) && config.Namespaces.PathOf(configs.NEWNET) == "" {
|
||||
config.Networks = []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
|
@ -233,7 +233,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
config.MountLabel = spec.Linux.MountLabel
|
||||
config.Sysctl = spec.Linux.Sysctl
|
||||
if spec.Linux.Seccomp != nil {
|
||||
seccomp, err := setupSeccomp(spec.Linux.Seccomp)
|
||||
seccomp, err := SetupSeccomp(spec.Linux.Seccomp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -243,8 +243,8 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
if spec.Process.SelinuxLabel != "" {
|
||||
config.ProcessLabel = spec.Process.SelinuxLabel
|
||||
}
|
||||
if spec.Process != nil && spec.Process.OOMScoreAdj != nil {
|
||||
config.OomScoreAdj = *spec.Process.OOMScoreAdj
|
||||
if spec.Process != nil {
|
||||
config.OomScoreAdj = spec.Process.OOMScoreAdj
|
||||
}
|
||||
if spec.Process.Capabilities != nil {
|
||||
config.Capabilities = &configs.Capabilities{
|
||||
|
@ -269,13 +269,17 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
|
|||
func createLibcontainerMount(cwd string, m specs.Mount) *configs.Mount {
|
||||
flags, pgflags, data, ext := parseMountOptions(m.Options)
|
||||
source := m.Source
|
||||
if m.Type == "bind" {
|
||||
device := m.Type
|
||||
if flags&unix.MS_BIND != 0 {
|
||||
if device == "" {
|
||||
device = "bind"
|
||||
}
|
||||
if !filepath.IsAbs(source) {
|
||||
source = filepath.Join(cwd, m.Source)
|
||||
}
|
||||
}
|
||||
return &configs.Mount{
|
||||
Device: m.Type,
|
||||
Device: device,
|
||||
Source: source,
|
||||
Destination: m.Destination,
|
||||
Data: data,
|
||||
|
@ -732,7 +736,7 @@ func parseMountOptions(options []string) (int, []int, string, int) {
|
|||
return flag, pgflag, strings.Join(data, ","), extFlags
|
||||
}
|
||||
|
||||
func setupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) {
|
||||
func SetupSeccomp(config *specs.LinuxSeccomp) (*configs.Seccomp, error) {
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/specconv/spec_linux_test.go
generated
vendored
|
@ -123,7 +123,7 @@ func TestSetupSeccomp(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
seccomp, err := setupSeccomp(conf)
|
||||
seccomp, err := SetupSeccomp(conf)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create Seccomp config: %v", err)
|
||||
|
|
2
vendor/github.com/opencontainers/runc/libcontainer/stacktrace/capture_test.go
generated
vendored
2
vendor/github.com/opencontainers/runc/libcontainer/stacktrace/capture_test.go
generated
vendored
|
@ -19,7 +19,7 @@ func TestCaptureTestFunc(t *testing.T) {
|
|||
// the first frame is the caller
|
||||
frame := stack.Frames[0]
|
||||
if expected := "captureFunc"; frame.Function != expected {
|
||||
t.Fatalf("expected function %q but recevied %q", expected, frame.Function)
|
||||
t.Fatalf("expected function %q but received %q", expected, frame.Function)
|
||||
}
|
||||
expected := "/runc/libcontainer/stacktrace"
|
||||
if !strings.HasSuffix(frame.Package, expected) {
|
||||
|
|
27
vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
generated
vendored
27
vendor/github.com/opencontainers/runc/libcontainer/standard_init_linux.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/opencontainers/runc/libcontainer/seccomp"
|
||||
"github.com/opencontainers/runc/libcontainer/system"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -49,11 +50,11 @@ func (l *linuxStandardInit) Init() error {
|
|||
// Do not inherit the parent's session keyring.
|
||||
sessKeyId, err := keys.JoinSessionKeyring(ringname)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "join session keyring")
|
||||
}
|
||||
// Make session keyring searcheable.
|
||||
if err := keys.ModKeyringPerm(sessKeyId, keepperms, newperms); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "mod keyring permissions")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +77,7 @@ func (l *linuxStandardInit) Init() error {
|
|||
return err
|
||||
}
|
||||
if err := system.Setctty(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "setctty")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,45 +90,45 @@ func (l *linuxStandardInit) Init() error {
|
|||
|
||||
if hostname := l.config.Config.Hostname; hostname != "" {
|
||||
if err := unix.Sethostname([]byte(hostname)); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "sethostname")
|
||||
}
|
||||
}
|
||||
if err := apparmor.ApplyProfile(l.config.AppArmorProfile); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "apply apparmor profile")
|
||||
}
|
||||
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "set process label")
|
||||
}
|
||||
|
||||
for key, value := range l.config.Config.Sysctl {
|
||||
if err := writeSystemProperty(key, value); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "write sysctl key %s", key)
|
||||
}
|
||||
}
|
||||
for _, path := range l.config.Config.ReadonlyPaths {
|
||||
if err := readonlyPath(path); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "readonly path %s", path)
|
||||
}
|
||||
}
|
||||
for _, path := range l.config.Config.MaskPaths {
|
||||
if err := maskPath(path, l.config.Config.MountLabel); err != nil {
|
||||
return err
|
||||
return errors.Wrapf(err, "mask path %s", path)
|
||||
}
|
||||
}
|
||||
pdeath, err := system.GetParentDeathSignal()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "get pdeath signal")
|
||||
}
|
||||
if l.config.NoNewPrivileges {
|
||||
if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "set nonewprivileges")
|
||||
}
|
||||
}
|
||||
// Tell our parent that we're ready to Execv. This must be done before the
|
||||
// Seccomp rules have been applied, because we need to be able to read and
|
||||
// write to a socket.
|
||||
if err := syncParentReady(l.pipe); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "sync ready")
|
||||
}
|
||||
// Without NoNewPrivileges seccomp is a privileged operation, so we need to
|
||||
// do this before dropping capabilities; otherwise do it as late as possible
|
||||
|
@ -143,7 +144,7 @@ func (l *linuxStandardInit) Init() error {
|
|||
// finalizeNamespace can change user/group which clears the parent death
|
||||
// signal, so we restore it here.
|
||||
if err := pdeath.Restore(); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "restore pdeath signal")
|
||||
}
|
||||
// Compare the parent from the initial start of the init process and make
|
||||
// sure that it did not change. if the parent changes that means it died
|
||||
|
|
38
vendor/github.com/opencontainers/runc/libcontainer/system/linux.go
generated
vendored
38
vendor/github.com/opencontainers/runc/libcontainer/system/linux.go
generated
vendored
|
@ -3,13 +3,12 @@
|
|||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall" // only for exec
|
||||
"unsafe"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
@ -102,34 +101,43 @@ func Setctty() error {
|
|||
}
|
||||
|
||||
// RunningInUserNS detects whether we are currently running in a user namespace.
|
||||
// Copied from github.com/lxc/lxd/shared/util.go
|
||||
// Originally copied from github.com/lxc/lxd/shared/util.go
|
||||
func RunningInUserNS() bool {
|
||||
file, err := os.Open("/proc/self/uid_map")
|
||||
uidmap, err := user.CurrentProcessUIDMap()
|
||||
if err != nil {
|
||||
// This kernel-provided file only exists if user namespaces are supported
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
return UIDMapInUserNS(uidmap)
|
||||
}
|
||||
|
||||
buf := bufio.NewReader(file)
|
||||
l, _, err := buf.ReadLine()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
line := string(l)
|
||||
var a, b, c int64
|
||||
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||
func UIDMapInUserNS(uidmap []user.IDMap) bool {
|
||||
/*
|
||||
* We assume we are in the initial user namespace if we have a full
|
||||
* range - 4294967295 uids starting at uid 0.
|
||||
*/
|
||||
if a == 0 && b == 0 && c == 4294967295 {
|
||||
if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetParentNSeuid returns the euid within the parent user namespace
|
||||
func GetParentNSeuid() int64 {
|
||||
euid := int64(os.Geteuid())
|
||||
uidmap, err := user.CurrentProcessUIDMap()
|
||||
if err != nil {
|
||||
// This kernel-provided file only exists if user namespaces are supported
|
||||
return euid
|
||||
}
|
||||
for _, um := range uidmap {
|
||||
if um.ID <= euid && euid <= um.ID+um.Count-1 {
|
||||
return um.ParentID + euid - um.ID
|
||||
}
|
||||
}
|
||||
return euid
|
||||
}
|
||||
|
||||
// SetSubreaper sets the value i as the subreaper setting for the calling process
|
||||
func SetSubreaper(i int) error {
|
||||
return unix.Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0)
|
||||
|
|
45
vendor/github.com/opencontainers/runc/libcontainer/system/linux_test.go
generated
vendored
Normal file
45
vendor/github.com/opencontainers/runc/libcontainer/system/linux_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// +build linux
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
)
|
||||
|
||||
func TestUIDMapInUserNS(t *testing.T) {
|
||||
cases := []struct {
|
||||
s string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
s: " 0 0 4294967295\n",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
s: " 0 0 1\n",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
s: " 0 1001 1\n 1 231072 65536\n",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// file exist but empty (the initial state when userns is created. see man 7 user_namespaces)
|
||||
s: "",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
uidmap, err := user.ParseIDMap(strings.NewReader(c.s))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := UIDMapInUserNS(uidmap)
|
||||
if c.expected != actual {
|
||||
t.Fatalf("expected %v, got %v for %q", c.expected, actual, c.s)
|
||||
}
|
||||
}
|
||||
}
|
18
vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go
generated
vendored
18
vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go
generated
vendored
|
@ -2,8 +2,26 @@
|
|||
|
||||
package system
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
)
|
||||
|
||||
// RunningInUserNS is a stub for non-Linux systems
|
||||
// Always returns false
|
||||
func RunningInUserNS() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// UIDMapInUserNS is a stub for non-Linux systems
|
||||
// Always returns false
|
||||
func UIDMapInUserNS(uidmap []user.IDMap) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetParentNSeuid returns the euid within the parent user namespace
|
||||
// Always returns os.Geteuid on non-linux
|
||||
func GetParentNSeuid() int {
|
||||
return os.Geteuid()
|
||||
}
|
||||
|
|
26
vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
generated
vendored
26
vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
generated
vendored
|
@ -114,3 +114,29 @@ func CurrentUser() (User, error) {
|
|||
func CurrentGroup() (Group, error) {
|
||||
return LookupGid(unix.Getgid())
|
||||
}
|
||||
|
||||
func CurrentUserSubUIDs() ([]SubID, error) {
|
||||
u, err := CurrentUser()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseSubIDFileFilter("/etc/subuid",
|
||||
func(entry SubID) bool { return entry.Name == u.Name })
|
||||
}
|
||||
|
||||
func CurrentGroupSubGIDs() ([]SubID, error) {
|
||||
g, err := CurrentGroup()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseSubIDFileFilter("/etc/subgid",
|
||||
func(entry SubID) bool { return entry.Name == g.Name })
|
||||
}
|
||||
|
||||
func CurrentProcessUIDMap() ([]IDMap, error) {
|
||||
return ParseIDMapFile("/proc/self/uid_map")
|
||||
}
|
||||
|
||||
func CurrentProcessGIDMap() ([]IDMap, error) {
|
||||
return ParseIDMapFile("/proc/self/gid_map")
|
||||
}
|
||||
|
|
133
vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
133
vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
|
@ -75,12 +75,29 @@ func groupFromOS(g *user.Group) (Group, error) {
|
|||
return newGroup, nil
|
||||
}
|
||||
|
||||
// SubID represents an entry in /etc/sub{u,g}id
|
||||
type SubID struct {
|
||||
Name string
|
||||
SubID int64
|
||||
Count int64
|
||||
}
|
||||
|
||||
// IDMap represents an entry in /proc/PID/{u,g}id_map
|
||||
type IDMap struct {
|
||||
ID int64
|
||||
ParentID int64
|
||||
Count int64
|
||||
}
|
||||
|
||||
func parseLine(line string, v ...interface{}) {
|
||||
if line == "" {
|
||||
parseParts(strings.Split(line, ":"), v...)
|
||||
}
|
||||
|
||||
func parseParts(parts []string, v ...interface{}) {
|
||||
if len(parts) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(line, ":")
|
||||
for i, p := range parts {
|
||||
// Ignore cases where we don't have enough fields to populate the arguments.
|
||||
// Some configuration files like to misbehave.
|
||||
|
@ -96,6 +113,8 @@ func parseLine(line string, v ...interface{}) {
|
|||
case *int:
|
||||
// "numbers", with conversion errors ignored because of some misbehaving configuration files.
|
||||
*e, _ = strconv.Atoi(p)
|
||||
case *int64:
|
||||
*e, _ = strconv.ParseInt(p, 10, 64)
|
||||
case *[]string:
|
||||
// Comma-separated lists.
|
||||
if p != "" {
|
||||
|
@ -105,7 +124,7 @@ func parseLine(line string, v ...interface{}) {
|
|||
}
|
||||
default:
|
||||
// Someone goof'd when writing code using this function. Scream so they can hear us.
|
||||
panic(fmt.Sprintf("parseLine only accepts {*string, *int, *[]string} as arguments! %#v is not a pointer!", e))
|
||||
panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -479,3 +498,111 @@ func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int
|
|||
}
|
||||
return GetAdditionalGroups(additionalGroups, group)
|
||||
}
|
||||
|
||||
func ParseSubIDFile(path string) ([]SubID, error) {
|
||||
subid, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer subid.Close()
|
||||
return ParseSubID(subid)
|
||||
}
|
||||
|
||||
func ParseSubID(subid io.Reader) ([]SubID, error) {
|
||||
return ParseSubIDFilter(subid, nil)
|
||||
}
|
||||
|
||||
func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) {
|
||||
subid, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer subid.Close()
|
||||
return ParseSubIDFilter(subid, filter)
|
||||
}
|
||||
|
||||
func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("nil source for subid-formatted data")
|
||||
}
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []SubID{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 5 subuid
|
||||
p := SubID{}
|
||||
parseLine(line, &p.Name, &p.SubID, &p.Count)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ParseIDMapFile(path string) ([]IDMap, error) {
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
return ParseIDMap(r)
|
||||
}
|
||||
|
||||
func ParseIDMap(r io.Reader) ([]IDMap, error) {
|
||||
return ParseIDMapFilter(r, nil)
|
||||
}
|
||||
|
||||
func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) {
|
||||
r, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
return ParseIDMapFilter(r, filter)
|
||||
}
|
||||
|
||||
func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("nil source for idmap-formatted data")
|
||||
}
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []IDMap{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 7 user_namespaces
|
||||
p := IDMap{}
|
||||
parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
|
15
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
15
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
|
@ -1,8 +1,6 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -17,19 +15,6 @@ const (
|
|||
exitSignalOffset = 128
|
||||
)
|
||||
|
||||
// GenerateRandomName returns a new name joined with a prefix. This size
|
||||
// specified is used to truncate the randomly generated value
|
||||
func GenerateRandomName(prefix string, size int) (string, error) {
|
||||
id := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if size > 64 {
|
||||
size = 64
|
||||
}
|
||||
return prefix + hex.EncodeToString(id)[:size], nil
|
||||
}
|
||||
|
||||
// ResolveRootfs ensures that the current working directory is
|
||||
// not a symlink and returns the absolute path to the rootfs
|
||||
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||
|
|
32
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_test.go
generated
vendored
32
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_test.go
generated
vendored
|
@ -10,28 +10,6 @@ import (
|
|||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func TestGenerateName(t *testing.T) {
|
||||
name, err := GenerateRandomName("veth", 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := 5 + len("veth")
|
||||
if len(name) != expected {
|
||||
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
|
||||
}
|
||||
|
||||
name, err = GenerateRandomName("veth", 65)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected = 64 + len("veth")
|
||||
if len(name) != expected {
|
||||
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
|
||||
}
|
||||
}
|
||||
|
||||
var labelTest = []struct {
|
||||
labels []string
|
||||
query string
|
||||
|
@ -151,4 +129,14 @@ func TestCleanPath(t *testing.T) {
|
|||
if path != "/var" {
|
||||
t.Errorf("expected to receive '/var' and received %s", path)
|
||||
}
|
||||
|
||||
path = CleanPath("/foo/bar/")
|
||||
if path != "/foo/bar" {
|
||||
t.Errorf("expected to receive '/foo/bar' and received %s", path)
|
||||
}
|
||||
|
||||
path = CleanPath("/foo/bar/../")
|
||||
if path != "/foo" {
|
||||
t.Errorf("expected to receive '/foo' and received %s", path)
|
||||
}
|
||||
}
|
||||
|
|
11
vendor/github.com/opencontainers/runc/main.go
generated
vendored
11
vendor/github.com/opencontainers/runc/main.go
generated
vendored
|
@ -63,7 +63,11 @@ func main() {
|
|||
app.Version = strings.Join(v, "\n")
|
||||
|
||||
root := "/run/runc"
|
||||
if os.Geteuid() != 0 {
|
||||
rootless, err := isRootless(nil)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if rootless {
|
||||
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||
if runtimeDir != "" {
|
||||
root = runtimeDir + "/runc"
|
||||
|
@ -108,6 +112,11 @@ func main() {
|
|||
Name: "systemd-cgroup",
|
||||
Usage: "enable systemd cgroup support, expects cgroupsPath to be of form \"slice:prefix:name\" for e.g. \"system.slice:runc:434234\"",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "rootless",
|
||||
Value: "auto",
|
||||
Usage: "enable rootless mode ('true', 'false', or 'auto')",
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
checkpointCommand,
|
||||
|
|
11
vendor/github.com/opencontainers/runc/man/README.md
generated
vendored
Normal file
11
vendor/github.com/opencontainers/runc/man/README.md
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
runc man pages
|
||||
====================
|
||||
|
||||
This directory contains man pages for runc in markdown format.
|
||||
|
||||
To generate man pages from it, use this command
|
||||
|
||||
./md2man-all.sh
|
||||
|
||||
You will see man pages generated under the man8 directory.
|
||||
|
27
vendor/github.com/opencontainers/runc/man/md2man-all.sh
generated
vendored
Executable file
27
vendor/github.com/opencontainers/runc/man/md2man-all.sh
generated
vendored
Executable file
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# get into this script's directory
|
||||
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"
|
||||
|
||||
[ "$1" = '-q' ] || {
|
||||
set -x
|
||||
pwd
|
||||
}
|
||||
|
||||
if ! ( which go-md2man &>/dev/null ); then
|
||||
echo "To install man pages, please install 'go-md2man'."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for FILE in *.md; do
|
||||
base="$(basename "$FILE")"
|
||||
name="${base%.md}"
|
||||
num="${name##*.}"
|
||||
if [ -z "$num" -o "$name" = "$num" ]; then
|
||||
# skip files that aren't of the format xxxx.N.md (like README.md)
|
||||
continue
|
||||
fi
|
||||
mkdir -p "./man${num}"
|
||||
go-md2man -in "$FILE" -out "./man${num}/${name}"
|
||||
done
|
25
vendor/github.com/opencontainers/runc/man/runc-checkpoint.8.md
generated
vendored
Normal file
25
vendor/github.com/opencontainers/runc/man/runc-checkpoint.8.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# NAME
|
||||
runc checkpoint - checkpoint a running container
|
||||
|
||||
# SYNOPSIS
|
||||
runc checkpoint [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
checkpointed.
|
||||
|
||||
# DESCRIPTION
|
||||
The checkpoint command saves the state of the container instance.
|
||||
|
||||
# OPTIONS
|
||||
--image-path value path for saving criu image files
|
||||
--work-path value path for saving work files and logs
|
||||
--parent-path value path for previous criu image files in pre-dump
|
||||
--leave-running leave the process running after checkpointing
|
||||
--tcp-established allow open tcp connections
|
||||
--ext-unix-sk allow external unix sockets
|
||||
--shell-job allow shell jobs
|
||||
--page-server value ADDRESS:PORT of the page server
|
||||
--file-locks handle file locks, for safety
|
||||
--pre-dump dump container's memory information only, leave the container running after this
|
||||
--manage-cgroups-mode value cgroups mode: 'soft' (default), 'full' and 'strict'
|
||||
--empty-ns value create a namespace, but don't restore its properties
|
27
vendor/github.com/opencontainers/runc/man/runc-create.8.md
generated
vendored
Normal file
27
vendor/github.com/opencontainers/runc/man/runc-create.8.md
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# NAME
|
||||
runc create - create a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc create [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container that you
|
||||
are starting. The name you provide for the container instance must be unique on
|
||||
your host.
|
||||
|
||||
# DESCRIPTION
|
||||
The create command creates an instance of a container for a bundle. The bundle
|
||||
is a directory with a specification file named "config.json" and a root
|
||||
filesystem.
|
||||
|
||||
The specification file includes an args parameter. The args parameter is used
|
||||
to specify command(s) that get run when the container is started. To change the
|
||||
command(s) that get executed on start, edit the args parameter of the spec. See
|
||||
"runc spec --help" for more explanation.
|
||||
|
||||
# OPTIONS
|
||||
--bundle value, -b value path to the root of the bundle directory, defaults to the current directory
|
||||
--console-socket value path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal
|
||||
--pid-file value specify the file to write the process id to
|
||||
--no-pivot do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk
|
||||
--no-new-keyring do not create a new session keyring for the container. This will cause the container to inherit the calling processes session key
|
||||
--preserve-fds value Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) (default: 0)
|
17
vendor/github.com/opencontainers/runc/man/runc-delete.8.md
generated
vendored
Normal file
17
vendor/github.com/opencontainers/runc/man/runc-delete.8.md
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# NAME
|
||||
runc delete - delete any resources held by the container often used with detached container
|
||||
|
||||
# SYNOPSIS
|
||||
runc delete [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container.
|
||||
|
||||
# OPTIONS
|
||||
--force, -f Forcibly deletes the container if it is still running (uses SIGKILL)
|
||||
|
||||
# EXAMPLE
|
||||
For example, if the container id is "ubuntu01" and runc list currently shows the
|
||||
status of "ubuntu01" as "stopped" the following will delete resources held for
|
||||
"ubuntu01" removing "ubuntu01" from the runc list of containers:
|
||||
|
||||
# runc delete ubuntu01
|
15
vendor/github.com/opencontainers/runc/man/runc-events.8.md
generated
vendored
Normal file
15
vendor/github.com/opencontainers/runc/man/runc-events.8.md
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# NAME
|
||||
runc events - display container events such as OOM notifications, cpu, memory, and IO usage statistics
|
||||
|
||||
# SYNOPSIS
|
||||
runc events [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container.
|
||||
|
||||
# DESCRIPTION
|
||||
The events command displays information about the container. By default the
|
||||
information is displayed once every 5 seconds.
|
||||
|
||||
# OPTIONS
|
||||
--interval value set the stats collection interval (default: 5s)
|
||||
--stats display the container's stats then exit
|
30
vendor/github.com/opencontainers/runc/man/runc-exec.8.md
generated
vendored
Normal file
30
vendor/github.com/opencontainers/runc/man/runc-exec.8.md
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# NAME
|
||||
runc exec - execute new process inside the container
|
||||
|
||||
# SYNOPSIS
|
||||
runc exec [command options] <container-id> -- <container command> [args...]
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container and
|
||||
"<container command>" is the command to be executed in the container.
|
||||
|
||||
# EXAMPLE
|
||||
For example, if the container is configured to run the linux ps command the
|
||||
following will output a list of processes running in the container:
|
||||
|
||||
# runc exec <container-id> ps
|
||||
|
||||
# OPTIONS
|
||||
--console value specify the pty slave path for use with the container
|
||||
--cwd value current working directory in the container
|
||||
--env value, -e value set environment variables
|
||||
--tty, -t allocate a pseudo-TTY
|
||||
--user value, -u value UID (format: <uid>[:<gid>])
|
||||
--additional-gids value, -g value additional gids
|
||||
--process value, -p value path to the process.json
|
||||
--detach, -d detach from the container's process
|
||||
--pid-file value specify the file to write the process id to
|
||||
--process-label value set the asm process label for the process commonly used with selinux
|
||||
--apparmor value set the apparmor profile for the process
|
||||
--no-new-privs set the no new privileges value for the process
|
||||
--cap value, -c value add a capability to the bounding set for the process
|
||||
--no-subreaper disable the use of the subreaper used to reap reparented processes
|
18
vendor/github.com/opencontainers/runc/man/runc-kill.8.md
generated
vendored
Normal file
18
vendor/github.com/opencontainers/runc/man/runc-kill.8.md
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# NAME
|
||||
runc kill - kill sends the specified signal (default: SIGTERM) to the container's init process
|
||||
|
||||
# SYNOPSIS
|
||||
runc kill [command options] <container-id> <signal>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container and
|
||||
"<signal>" is the signal to be sent to the init process.
|
||||
|
||||
# OPTIONS
|
||||
--all, -a send the specified signal to all processes inside the container
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
For example, if the container id is "ubuntu01" the following will send a "KILL"
|
||||
signal to the init process of the "ubuntu01" container:
|
||||
|
||||
# runc kill ubuntu01 KILL
|
19
vendor/github.com/opencontainers/runc/man/runc-list.8.md
generated
vendored
Normal file
19
vendor/github.com/opencontainers/runc/man/runc-list.8.md
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
# NAME
|
||||
runc list - lists containers started by runc with the given root
|
||||
|
||||
# SYNOPSIS
|
||||
runc list [command options]
|
||||
|
||||
# EXAMPLE
|
||||
Where the given root is specified via the global option "--root"
|
||||
(default: "/run/runc").
|
||||
|
||||
To list containers created via the default "--root":
|
||||
# runc list
|
||||
|
||||
To list containers created using a non-default value for "--root":
|
||||
# runc --root value list
|
||||
|
||||
# OPTIONS
|
||||
--format value, -f value select one of: table or json (default: "table")
|
||||
--quiet, -q display only container IDs
|
12
vendor/github.com/opencontainers/runc/man/runc-pause.8.md
generated
vendored
Normal file
12
vendor/github.com/opencontainers/runc/man/runc-pause.8.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# NAME
|
||||
runc pause - pause suspends all processes inside the container
|
||||
|
||||
# SYNOPSIS
|
||||
runc pause <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
paused.
|
||||
|
||||
# DESCRIPTION
|
||||
The pause command suspends all processes in the instance of the container.
|
||||
Use runc list to identiy instances of containers and their current status.
|
13
vendor/github.com/opencontainers/runc/man/runc-ps.8.md
generated
vendored
Normal file
13
vendor/github.com/opencontainers/runc/man/runc-ps.8.md
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# NAME
|
||||
runc ps - ps displays the processes running inside a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc ps [command options] <container-id> [ps options]
|
||||
|
||||
# OPTIONS
|
||||
--format value, -f value select one of: table(default) or json
|
||||
|
||||
The default format is table. The following will output the processes of a container
|
||||
in json format:
|
||||
|
||||
# runc ps -f json <container-id>
|
26
vendor/github.com/opencontainers/runc/man/runc-restore.8.md
generated
vendored
Normal file
26
vendor/github.com/opencontainers/runc/man/runc-restore.8.md
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
# NAME
|
||||
runc restore - restore a container from a previous checkpoint
|
||||
|
||||
# SYNOPSIS
|
||||
runc restore [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
restored.
|
||||
|
||||
# DESCRIPTION
|
||||
Restores the saved state of the container instance that was previously saved
|
||||
using the runc checkpoint command.
|
||||
|
||||
# OPTIONS
|
||||
--image-path value path to criu image files for restoring
|
||||
--work-path value path for saving work files and logs
|
||||
--tcp-established allow open tcp connections
|
||||
--ext-unix-sk allow external unix sockets
|
||||
--shell-job allow shell jobs
|
||||
--file-locks handle file locks, for safety
|
||||
--manage-cgroups-mode value cgroups mode: 'soft' (default), 'full' and 'strict'
|
||||
--bundle value, -b value path to the root of the bundle directory
|
||||
--detach, -d detach from the container's process
|
||||
--pid-file value specify the file to write the process id to
|
||||
--no-subreaper disable the use of the subreaper used to reap reparented processes
|
||||
--no-pivot do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk
|
12
vendor/github.com/opencontainers/runc/man/runc-resume.8.md
generated
vendored
Normal file
12
vendor/github.com/opencontainers/runc/man/runc-resume.8.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# NAME
|
||||
runc resume - resumes all processes that have been previously paused
|
||||
|
||||
# SYNOPSIS
|
||||
runc resume <container-id>
|
||||
|
||||
Where "<container-id>" is the name for the instance of the container to be
|
||||
resumed.
|
||||
|
||||
# DESCRIPTION
|
||||
The resume command resumes all processes in the instance of the container.
|
||||
Use runc list to identiy instances of containers and their current status.
|
29
vendor/github.com/opencontainers/runc/man/runc-run.8.md
generated
vendored
Normal file
29
vendor/github.com/opencontainers/runc/man/runc-run.8.md
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# NAME
|
||||
runc run - create and run a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc run [command options] <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container that you
|
||||
are starting. The name you provide for the container instance must be unique on
|
||||
your host.
|
||||
|
||||
# DESCRIPTION
|
||||
The run command creates an instance of a container for a bundle. The bundle
|
||||
is a directory with a specification file named "config.json" and a root
|
||||
filesystem.
|
||||
|
||||
The specification file includes an args parameter. The args parameter is used
|
||||
to specify command(s) that get run when the container is started. To change the
|
||||
command(s) that get executed on start, edit the args parameter of the spec. See
|
||||
"runc spec --help" for more explanation.
|
||||
|
||||
# OPTIONS
|
||||
--bundle value, -b value path to the root of the bundle directory, defaults to the current directory
|
||||
--console-socket value path to an AF_UNIX socket which will receive a file descriptor referencing the master end of the console's pseudoterminal
|
||||
--detach, -d detach from the container's process
|
||||
--pid-file value specify the file to write the process id to
|
||||
--no-subreaper disable the use of the subreaper used to reap reparented processes
|
||||
--no-pivot do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk
|
||||
--no-new-keyring do not create a new session keyring for the container. This will cause the container to inherit the calling processes session key
|
||||
--preserve-fds value Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) (default: 0)
|
52
vendor/github.com/opencontainers/runc/man/runc-spec.8.md
generated
vendored
Normal file
52
vendor/github.com/opencontainers/runc/man/runc-spec.8.md
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
# NAME
|
||||
runc spec - create a new specification file
|
||||
|
||||
# SYNOPSIS
|
||||
runc spec [command options] [arguments...]
|
||||
|
||||
# DESCRIPTION
|
||||
The spec command creates the new specification file named "config.json" for
|
||||
the bundle.
|
||||
|
||||
The spec generated is just a starter file. Editing of the spec is required to
|
||||
achieve desired results. For example, the newly generated spec includes an args
|
||||
parameter that is initially set to call the "sh" command when the container is
|
||||
started. Calling "sh" may work for an ubuntu container or busybox, but will not
|
||||
work for containers that do not include the "sh" program.
|
||||
|
||||
# EXAMPLE
|
||||
To run docker's hello-world container one needs to set the args parameter
|
||||
in the spec to call hello. This can be done using the sed command or a text
|
||||
editor. The following commands create a bundle for hello-world, change the
|
||||
default args parameter in the spec from "sh" to "/hello", then run the hello
|
||||
command in a new hello-world container named container1:
|
||||
|
||||
mkdir hello
|
||||
cd hello
|
||||
docker pull hello-world
|
||||
docker export $(docker create hello-world) > hello-world.tar
|
||||
mkdir rootfs
|
||||
tar -C rootfs -xf hello-world.tar
|
||||
runc spec
|
||||
sed -i 's;"sh";"/hello";' config.json
|
||||
runc start container1
|
||||
|
||||
In the start command above, "container1" is the name for the instance of the
|
||||
container that you are starting. The name you provide for the container instance
|
||||
must be unique on your host.
|
||||
|
||||
An alternative for generating a customized spec config is to use "oci-runtime-tool", the
|
||||
sub-command "oci-runtime-tool generate" has lots of options that can be used to do any
|
||||
customizations as you want, see [runtime-tools](https://github.com/opencontainers/runtime-tools)
|
||||
to get more information.
|
||||
|
||||
When starting a container through runc, runc needs root privilege. If not
|
||||
already running as root, you can use sudo to give runc root privilege. For
|
||||
example: "sudo runc start container1" will give runc root privilege to start the
|
||||
container on your host.
|
||||
|
||||
Alternatively, you can start a rootless container, which has the ability to run without root privileges. For this to work, the specification file needs to be adjusted accordingly. You can pass the parameter --rootless to this command to generate a proper rootless spec file.
|
||||
|
||||
# OPTIONS
|
||||
--bundle value, -b value path to the root of the bundle directory
|
||||
--rootless generate a configuration for a rootless container
|
12
vendor/github.com/opencontainers/runc/man/runc-start.8.md
generated
vendored
Normal file
12
vendor/github.com/opencontainers/runc/man/runc-start.8.md
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# NAME
|
||||
runc start - start executes the user defined process in a created container
|
||||
|
||||
# SYNOPSIS
|
||||
runc start <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container that you
|
||||
are starting. The name you provide for the container instance must be unique on
|
||||
your host.
|
||||
|
||||
# DESCRIPTION
|
||||
The start command executes the user defined process in a created container.
|
11
vendor/github.com/opencontainers/runc/man/runc-state.8.md
generated
vendored
Normal file
11
vendor/github.com/opencontainers/runc/man/runc-state.8.md
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
# NAME
|
||||
runc state - output the state of a container
|
||||
|
||||
# SYNOPSIS
|
||||
runc state <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container.
|
||||
|
||||
# DESCRIPTION
|
||||
The state command outputs current state information for the
|
||||
instance of a container.
|
51
vendor/github.com/opencontainers/runc/man/runc-update.8.md
generated
vendored
Normal file
51
vendor/github.com/opencontainers/runc/man/runc-update.8.md
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
# NAME
|
||||
runc update - update container resource constraints
|
||||
|
||||
# SYNOPSIS
|
||||
runc update [command options] <container-id>
|
||||
|
||||
# DESCRIPTION
|
||||
The data can be read from a file or the standard input, the
|
||||
accepted format is as follow (unchanged values can be omitted):
|
||||
|
||||
{
|
||||
"memory": {
|
||||
"limit": 0,
|
||||
"reservation": 0,
|
||||
"swap": 0,
|
||||
"kernel": 0,
|
||||
"kernelTCP": 0
|
||||
},
|
||||
"cpu": {
|
||||
"shares": 0,
|
||||
"quota": 0,
|
||||
"period": 0,
|
||||
"realtimeRuntime": 0,
|
||||
"realtimePeriod": 0,
|
||||
"cpus": "",
|
||||
"mems": ""
|
||||
},
|
||||
"blockIO": {
|
||||
"blkioWeight": 0
|
||||
}
|
||||
}
|
||||
|
||||
Note: if data is to be read from a file or the standard input, all
|
||||
other options are ignored.
|
||||
|
||||
# OPTIONS
|
||||
--resources value, -r value path to the file containing the resources to update or '-' to read from the standard input
|
||||
--blkio-weight value Specifies per cgroup weight, range is from 10 to 1000 (default: 0)
|
||||
--cpu-period value CPU CFS period to be used for hardcapping (in usecs). 0 to use system default
|
||||
--cpu-quota value CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period
|
||||
--cpu-rt-period value CPU realtime period to be used for hardcapping (in usecs). 0 to use system default
|
||||
--cpu-rt-runtime value CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period
|
||||
--cpu-share value CPU shares (relative weight vs. other containers)
|
||||
--cpuset-cpus value CPU(s) to use
|
||||
--cpuset-mems value Memory node(s) to use
|
||||
--kernel-memory value Kernel memory limit (in bytes)
|
||||
--kernel-memory-tcp value Kernel memory limit (in bytes) for tcp buffer
|
||||
--memory value Memory limit (in bytes)
|
||||
--memory-reservation value Memory reservation or soft_limit (in bytes)
|
||||
--memory-swap value Total memory usage (memory + swap); set '-1' to enable unlimited swap
|
||||
--pids-limit value Maximum number of pids allowed in the container (default: 0)
|
59
vendor/github.com/opencontainers/runc/man/runc.8.md
generated
vendored
Normal file
59
vendor/github.com/opencontainers/runc/man/runc.8.md
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
# NAME
|
||||
runc - Open Container Initiative runtime
|
||||
|
||||
# SYNOPSIS
|
||||
runc [global options] command [command options] [arguments...]
|
||||
|
||||
# DESCRIPTION
|
||||
runc is a command line client for running applications packaged according to
|
||||
the Open Container Initiative (OCI) format and is a compliant implementation of the
|
||||
Open Container Initiative specification.
|
||||
|
||||
runc integrates well with existing process supervisors to provide a production
|
||||
container runtime environment for applications. It can be used with your
|
||||
existing process monitoring tools and the container will be spawned as a
|
||||
direct child of the process supervisor.
|
||||
|
||||
Containers are configured using bundles. A bundle for a container is a directory
|
||||
that includes a specification file named "config.json" and a root filesystem.
|
||||
The root filesystem contains the contents of the container.
|
||||
|
||||
To start a new instance of a container:
|
||||
|
||||
# runc start [ -b bundle ] <container-id>
|
||||
|
||||
Where "<container-id>" is your name for the instance of the container that you
|
||||
are starting. The name you provide for the container instance must be unique on
|
||||
your host. Providing the bundle directory using "-b" is optional. The default
|
||||
value for "bundle" is the current directory.
|
||||
|
||||
# COMMANDS
|
||||
checkpoint checkpoint a running container
|
||||
create create a container
|
||||
delete delete any resources held by the container often used with detached containers
|
||||
events display container events such as OOM notifications, cpu, memory, IO and network stats
|
||||
exec execute new process inside the container
|
||||
init initialize the namespaces and launch the process (do not call it outside of runc)
|
||||
kill kill sends the specified signal (default: SIGTERM) to the container's init process
|
||||
list lists containers started by runc with the given root
|
||||
pause pause suspends all processes inside the container
|
||||
ps displays the processes running inside a container
|
||||
restore restore a container from a previous checkpoint
|
||||
resume resumes all processes that have been previously paused
|
||||
run create and run a container
|
||||
spec create a new specification file
|
||||
start executes the user defined process in a created container
|
||||
state output the state of a container
|
||||
update update container resource constraints
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
# GLOBAL OPTIONS
|
||||
--debug enable debug output for logging
|
||||
--log value set the log file path where internal debug information is written (default: "/dev/null")
|
||||
--log-format value set the format used by logs ('text' (default), or 'json') (default: "text")
|
||||
--root value root directory for storage of container state (this should be located in tmpfs) (default: "/run/runc" or $XDG_RUNTIME_DIR/runc for rootless containers)
|
||||
--criu value path to the criu binary used for checkpoint and restore (default: "criu")
|
||||
--systemd-cgroup enable systemd cgroup support, expects cgroupsPath to be of form "slice:prefix:name" for e.g. "system.slice:runc:434234"
|
||||
--rootless value enable rootless mode ('true', 'false', or 'auto') (default: "auto")
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
2
vendor/github.com/opencontainers/runc/notify_socket.go
generated
vendored
2
vendor/github.com/opencontainers/runc/notify_socket.go
generated
vendored
|
@ -44,7 +44,7 @@ func (ns *notifySocket) Close() error {
|
|||
// If systemd is supporting sd_notify protocol, this function will add support
|
||||
// for sd_notify protocol from within the container.
|
||||
func (s *notifySocket) setupSpec(context *cli.Context, spec *specs.Spec) {
|
||||
mount := specs.Mount{Destination: s.host, Type: "bind", Source: s.socketPath, Options: []string{"bind"}}
|
||||
mount := specs.Mount{Destination: s.host, Source: s.socketPath, Options: []string{"bind"}}
|
||||
spec.Mounts = append(spec.Mounts, mount)
|
||||
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", s.host))
|
||||
}
|
||||
|
|
6
vendor/github.com/opencontainers/runc/ps.go
generated
vendored
6
vendor/github.com/opencontainers/runc/ps.go
generated
vendored
|
@ -29,7 +29,11 @@ var psCommand = cli.Command{
|
|||
return err
|
||||
}
|
||||
// XXX: Currently not supported with rootless containers.
|
||||
if isRootless() {
|
||||
rootless, err := isRootless(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rootless {
|
||||
return fmt.Errorf("runc ps requires root")
|
||||
}
|
||||
|
||||
|
|
9
vendor/github.com/opencontainers/runc/restore.go
generated
vendored
9
vendor/github.com/opencontainers/runc/restore.go
generated
vendored
|
@ -96,7 +96,11 @@ using the runc checkpoint command.`,
|
|||
return err
|
||||
}
|
||||
// XXX: Currently this is untested with rootless containers.
|
||||
if isRootless() {
|
||||
rootless, err := isRootless(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rootless {
|
||||
return fmt.Errorf("runc restore requires root")
|
||||
}
|
||||
|
||||
|
@ -105,6 +109,9 @@ using the runc checkpoint command.`,
|
|||
return err
|
||||
}
|
||||
options := criuOptions(context)
|
||||
if err := setEmptyNsMask(context, options); err != nil {
|
||||
return err
|
||||
}
|
||||
status, err := startContainer(context, spec, CT_ACT_RESTORE, options)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
33
vendor/github.com/opencontainers/runc/script/.validate
generated
vendored
Normal file
33
vendor/github.com/opencontainers/runc/script/.validate
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -z "$VALIDATE_UPSTREAM" ]; then
|
||||
# this is kind of an expensive check, so let's not do this twice if we
|
||||
# are running more than one validate bundlescript
|
||||
|
||||
VALIDATE_REPO='https://github.com/opencontainers/runc.git'
|
||||
VALIDATE_BRANCH='master'
|
||||
|
||||
if [ "$TRAVIS" = 'true' -a "$TRAVIS_PULL_REQUEST" != 'false' ]; then
|
||||
VALIDATE_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git"
|
||||
VALIDATE_BRANCH="${TRAVIS_BRANCH}"
|
||||
fi
|
||||
|
||||
VALIDATE_HEAD="$(git rev-parse --verify HEAD)"
|
||||
|
||||
git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH"
|
||||
VALIDATE_UPSTREAM="$(git rev-parse --verify FETCH_HEAD)"
|
||||
|
||||
VALIDATE_COMMIT_LOG="$VALIDATE_UPSTREAM..$VALIDATE_HEAD"
|
||||
VALIDATE_COMMIT_DIFF="$VALIDATE_UPSTREAM...$VALIDATE_HEAD"
|
||||
|
||||
validate_diff() {
|
||||
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
|
||||
git diff "$VALIDATE_COMMIT_DIFF" "$@"
|
||||
fi
|
||||
}
|
||||
validate_log() {
|
||||
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
|
||||
git log "$VALIDATE_COMMIT_LOG" "$@"
|
||||
fi
|
||||
}
|
||||
fi
|
247
vendor/github.com/opencontainers/runc/script/check-config.sh
generated
vendored
Executable file
247
vendor/github.com/opencontainers/runc/script/check-config.sh
generated
vendored
Executable file
|
@ -0,0 +1,247 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# bits of this were adapted from check_config.sh in docker
|
||||
# see also https://github.com/docker/docker/blob/master/contrib/check-config.sh
|
||||
|
||||
possibleConfigs=(
|
||||
'/proc/config.gz'
|
||||
"/boot/config-$(uname -r)"
|
||||
"/usr/src/linux-$(uname -r)/.config"
|
||||
'/usr/src/linux/.config'
|
||||
)
|
||||
possibleConfigFiles=(
|
||||
'config.gz'
|
||||
"config-$(uname -r)"
|
||||
'.config'
|
||||
)
|
||||
|
||||
if ! command -v zgrep &>/dev/null; then
|
||||
zgrep() {
|
||||
zcat "$2" | grep "$1"
|
||||
}
|
||||
fi
|
||||
|
||||
kernelVersion="$(uname -r)"
|
||||
kernelMajor="${kernelVersion%%.*}"
|
||||
kernelMinor="${kernelVersion#$kernelMajor.}"
|
||||
kernelMinor="${kernelMinor%%.*}"
|
||||
|
||||
is_set() {
|
||||
zgrep "CONFIG_$1=[y|m]" "$CONFIG" >/dev/null
|
||||
}
|
||||
is_set_in_kernel() {
|
||||
zgrep "CONFIG_$1=y" "$CONFIG" >/dev/null
|
||||
}
|
||||
is_set_as_module() {
|
||||
zgrep "CONFIG_$1=m" "$CONFIG" >/dev/null
|
||||
}
|
||||
|
||||
color() {
|
||||
local codes=()
|
||||
if [ "$1" = 'bold' ]; then
|
||||
codes=("${codes[@]}" '1')
|
||||
shift
|
||||
fi
|
||||
if [ "$#" -gt 0 ]; then
|
||||
local code
|
||||
case "$1" in
|
||||
# see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
black) code=30 ;;
|
||||
red) code=31 ;;
|
||||
green) code=32 ;;
|
||||
yellow) code=33 ;;
|
||||
blue) code=34 ;;
|
||||
magenta) code=35 ;;
|
||||
cyan) code=36 ;;
|
||||
white) code=37 ;;
|
||||
esac
|
||||
if [ "$code" ]; then
|
||||
codes=("${codes[@]}" "$code")
|
||||
fi
|
||||
fi
|
||||
local IFS=';'
|
||||
echo -en '\033['"${codes[*]}"'m'
|
||||
}
|
||||
wrap_color() {
|
||||
text="$1"
|
||||
shift
|
||||
color "$@"
|
||||
echo -n "$text"
|
||||
color reset
|
||||
echo
|
||||
}
|
||||
|
||||
wrap_good() {
|
||||
echo "$(wrap_color "$1" white): $(wrap_color "$2" green)"
|
||||
}
|
||||
wrap_bad() {
|
||||
echo "$(wrap_color "$1" bold): $(wrap_color "$2" bold red)"
|
||||
}
|
||||
wrap_warning() {
|
||||
wrap_color >&2 "$*" red
|
||||
}
|
||||
|
||||
check_flag() {
|
||||
if is_set_in_kernel "$1"; then
|
||||
wrap_good "CONFIG_$1" 'enabled'
|
||||
elif is_set_as_module "$1"; then
|
||||
wrap_good "CONFIG_$1" 'enabled (as module)'
|
||||
else
|
||||
wrap_bad "CONFIG_$1" 'missing'
|
||||
fi
|
||||
}
|
||||
|
||||
check_flags() {
|
||||
for flag in "$@"; do
|
||||
echo "- $(check_flag "$flag")"
|
||||
done
|
||||
}
|
||||
|
||||
check_distro_userns() {
|
||||
source /etc/os-release 2>/dev/null || /bin/true
|
||||
if [[ "${ID}" =~ ^(centos|rhel)$ && "${VERSION_ID}" =~ ^7 ]]; then
|
||||
# this is a CentOS7 or RHEL7 system
|
||||
grep -q "user_namespace.enable=1" /proc/cmdline || {
|
||||
# no user namespace support enabled
|
||||
wrap_bad " (RHEL7/CentOS7" "User namespaces disabled; add 'user_namespace.enable=1' to boot command line)"
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
is_config() {
|
||||
local config="$1"
|
||||
|
||||
# Todo: more check
|
||||
[[ -f "$config" ]] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
search_config() {
|
||||
local target_dir="$1"
|
||||
[[ "$target_dir" ]] || target_dir=("${possibleConfigs[@]}")
|
||||
|
||||
local tryConfig
|
||||
for tryConfig in "${target_dir[@]}"; do
|
||||
is_config "$tryConfig" && {
|
||||
CONFIG="$tryConfig"
|
||||
return
|
||||
}
|
||||
[[ -d "$tryConfig" ]] && {
|
||||
for tryFile in "${possibleConfigFiles[@]}"; do
|
||||
is_config "$tryConfig/$tryFile" && {
|
||||
CONFIG="$tryConfig/$tryFile"
|
||||
return
|
||||
}
|
||||
done
|
||||
}
|
||||
done
|
||||
|
||||
wrap_warning "error: cannot find kernel config"
|
||||
wrap_warning " try running this script again, specifying the kernel config:"
|
||||
wrap_warning " CONFIG=/path/to/kernel/.config $0 or $0 /path/to/kernel/.config"
|
||||
exit 1
|
||||
}
|
||||
|
||||
CONFIG="$1"
|
||||
|
||||
is_config "$CONFIG" || {
|
||||
if [[ ! "$CONFIG" ]]; then
|
||||
wrap_color "info: no config specified, searching for kernel config ..." white
|
||||
search_config
|
||||
elif [[ -d "$CONFIG" ]]; then
|
||||
wrap_color "info: input is a directory, searching for kernel config in this directory..." white
|
||||
search_config "$CONFIG"
|
||||
else
|
||||
wrap_warning "warning: $CONFIG seems not a kernel config, searching other paths for kernel config ..."
|
||||
search_config
|
||||
fi
|
||||
}
|
||||
|
||||
wrap_color "info: reading kernel config from $CONFIG ..." white
|
||||
echo
|
||||
|
||||
echo 'Generally Necessary:'
|
||||
|
||||
echo -n '- '
|
||||
cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)[, ]/ && $3 == "cgroup" { print $2 }' /proc/mounts | head -n1)"
|
||||
cgroupDir="$(dirname "$cgroupSubsystemDir")"
|
||||
if [ -d "$cgroupDir/cpu" -o -d "$cgroupDir/cpuacct" -o -d "$cgroupDir/cpuset" -o -d "$cgroupDir/devices" -o -d "$cgroupDir/freezer" -o -d "$cgroupDir/memory" ]; then
|
||||
echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]"
|
||||
else
|
||||
if [ "$cgroupSubsystemDir" ]; then
|
||||
echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]"
|
||||
else
|
||||
echo "$(wrap_bad 'cgroup hierarchy' 'nonexistent??')"
|
||||
fi
|
||||
echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)"
|
||||
fi
|
||||
|
||||
if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then
|
||||
echo -n '- '
|
||||
if command -v apparmor_parser &>/dev/null; then
|
||||
echo "$(wrap_good 'apparmor' 'enabled and tools installed')"
|
||||
else
|
||||
echo "$(wrap_bad 'apparmor' 'enabled, but apparmor_parser missing')"
|
||||
echo -n ' '
|
||||
if command -v apt-get &>/dev/null; then
|
||||
echo "$(wrap_color '(use "apt-get install apparmor" to fix this)')"
|
||||
elif command -v yum &>/dev/null; then
|
||||
echo "$(wrap_color '(your best bet is "yum install apparmor-parser")')"
|
||||
else
|
||||
echo "$(wrap_color '(look for an "apparmor" package for your distribution)')"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
flags=(
|
||||
NAMESPACES {NET,PID,IPC,UTS}_NS
|
||||
CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG
|
||||
KEYS
|
||||
MACVLAN VETH BRIDGE BRIDGE_NETFILTER
|
||||
NF_NAT_IPV4 IP_NF_FILTER IP_NF_TARGET_MASQUERADE
|
||||
NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK}
|
||||
NF_NAT NF_NAT_NEEDED
|
||||
|
||||
# required for bind-mounting /dev/mqueue into containers
|
||||
POSIX_MQUEUE
|
||||
)
|
||||
check_flags "${flags[@]}"
|
||||
echo
|
||||
|
||||
echo 'Optional Features:'
|
||||
{
|
||||
check_flags USER_NS
|
||||
check_distro_userns
|
||||
|
||||
check_flags SECCOMP
|
||||
check_flags CGROUP_PIDS
|
||||
|
||||
check_flags MEMCG_SWAP MEMCG_SWAP_ENABLED
|
||||
if is_set MEMCG_SWAP && ! is_set MEMCG_SWAP_ENABLED; then
|
||||
echo " $(wrap_color '(note that cgroup swap accounting is not enabled in your kernel config, you can enable it by setting boot option "swapaccount=1")' bold black)"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -le 5 ]; then
|
||||
check_flags MEMCG_KMEM
|
||||
fi
|
||||
|
||||
if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 18 ]; then
|
||||
check_flags RESOURCE_COUNTERS
|
||||
fi
|
||||
|
||||
if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 13 ]; then
|
||||
netprio=NETPRIO_CGROUP
|
||||
else
|
||||
netprio=CGROUP_NET_PRIO
|
||||
fi
|
||||
|
||||
flags=(
|
||||
BLK_CGROUP BLK_DEV_THROTTLING IOSCHED_CFQ CFQ_GROUP_IOSCHED
|
||||
CGROUP_PERF
|
||||
CGROUP_HUGETLB
|
||||
NET_CLS_CGROUP $netprio
|
||||
CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED
|
||||
)
|
||||
check_flags "${flags[@]}"
|
130
vendor/github.com/opencontainers/runc/script/release.sh
generated
vendored
Executable file
130
vendor/github.com/opencontainers/runc/script/release.sh
generated
vendored
Executable file
|
@ -0,0 +1,130 @@
|
|||
#!/bin/bash
|
||||
# Copyright (C) 2017 SUSE LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
## --->
|
||||
# Project-specific options and functions. In *theory* you shouldn't need to
|
||||
# touch anything else in this script in order to use this elsewhere.
|
||||
project="runc"
|
||||
root="$(readlink -f "$(dirname "${BASH_SOURCE}")/..")"
|
||||
|
||||
# This function takes an output path as an argument, where the built
|
||||
# (preferably static) binary should be placed.
|
||||
function build_project() {
|
||||
builddir="$(dirname "$1")"
|
||||
|
||||
# Build with all tags enabled.
|
||||
make -C "$root" COMMIT_NO= BUILDTAGS="seccomp selinux apparmor" static
|
||||
mv "$root/$project" "$1"
|
||||
}
|
||||
|
||||
# End of the easy-to-configure portion.
|
||||
## <---
|
||||
|
||||
# Print usage information.
|
||||
function usage() {
|
||||
echo "usage: release.sh [-S <gpg-key-id>] [-c <commit-ish>] [-r <release-dir>] [-v <version>]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Log something to stderr.
|
||||
function log() {
|
||||
echo "[*] $*" >&2
|
||||
}
|
||||
|
||||
# Log something to stderr and then exit with 0.
|
||||
function bail() {
|
||||
log "$@"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Conduct a sanity-check to make sure that GPG provided with the given
|
||||
# arguments can sign something. Inability to sign things is not a fatal error.
|
||||
function gpg_cansign() {
|
||||
gpg "$@" --clear-sign </dev/null >/dev/null
|
||||
}
|
||||
|
||||
# When creating releases we need to build static binaries, an archive of the
|
||||
# current commit, and generate detached signatures for both.
|
||||
keyid=""
|
||||
commit="HEAD"
|
||||
version=""
|
||||
releasedir=""
|
||||
hashcmd=""
|
||||
while getopts "S:c:r:v:h:" opt; do
|
||||
case "$opt" in
|
||||
S)
|
||||
keyid="$OPTARG"
|
||||
;;
|
||||
c)
|
||||
commit="$OPTARG"
|
||||
;;
|
||||
r)
|
||||
releasedir="$OPTARG"
|
||||
;;
|
||||
v)
|
||||
version="$OPTARG"
|
||||
;;
|
||||
h)
|
||||
hashcmd="$OPTARG"
|
||||
;;
|
||||
\:)
|
||||
echo "Missing argument: -$OPTARG" >&2
|
||||
usage
|
||||
;;
|
||||
\?)
|
||||
echo "Invalid option: -$OPTARG" >&2
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
version="${version:-$(<"$root/VERSION")}"
|
||||
releasedir="${releasedir:-release/$version}"
|
||||
hashcmd="${hashcmd:-sha256sum}"
|
||||
goarch="$(go env GOARCH || echo "amd64")"
|
||||
|
||||
log "creating $project release in '$releasedir'"
|
||||
log " version: $version"
|
||||
log " commit: $commit"
|
||||
log " key: ${keyid:-DEFAULT}"
|
||||
log " hash: $hashcmd"
|
||||
|
||||
# Make explicit what we're doing.
|
||||
set -x
|
||||
|
||||
# Make the release directory.
|
||||
rm -rf "$releasedir" && mkdir -p "$releasedir"
|
||||
|
||||
# Build project.
|
||||
build_project "$releasedir/$project.$goarch"
|
||||
|
||||
# Generate new archive.
|
||||
git archive --format=tar --prefix="$project-$version/" "$commit" | xz > "$releasedir/$project.tar.xz"
|
||||
|
||||
# Generate sha256 checksums for both.
|
||||
( cd "$releasedir" ; "$hashcmd" "$project".{"$goarch",tar.xz} > "$project.$hashcmd" ; )
|
||||
|
||||
# Set up the gpgflags.
|
||||
[[ "$keyid" ]] && export gpgflags="--default-key $keyid"
|
||||
gpg_cansign $gpgflags || bail "Could not find suitable GPG key, skipping signing step."
|
||||
|
||||
# Sign everything.
|
||||
gpg $gpgflags --detach-sign --armor "$releasedir/$project.$goarch"
|
||||
gpg $gpgflags --detach-sign --armor "$releasedir/$project.tar.xz"
|
||||
gpg $gpgflags --clear-sign --armor \
|
||||
--output "$releasedir/$project.$hashcmd"{.tmp,} && \
|
||||
mv "$releasedir/$project.$hashcmd"{.tmp,}
|
4
vendor/github.com/opencontainers/runc/script/tmpmount
generated
vendored
Executable file
4
vendor/github.com/opencontainers/runc/script/tmpmount
generated
vendored
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
mount -t tmpfs none /tmp
|
||||
exec "$@"
|
42
vendor/github.com/opencontainers/runc/script/validate-c
generated
vendored
Executable file
42
vendor/github.com/opencontainers/runc/script/validate-c
generated
vendored
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/bin/bash
|
||||
|
||||
source "$(dirname "$BASH_SOURCE")/.validate"
|
||||
|
||||
IFS=$'\n'
|
||||
files=($(validate_diff --diff-filter=ACMR --name-only -- '*.c' | grep -v '^vendor/' || true))
|
||||
unset IFS
|
||||
|
||||
# indent(1): "You must use the ‘-T’ option to tell indent the name of all the typenames in your program that are defined by typedef."
|
||||
INDENT="indent -linux -l120 -T size_t -T jmp_buf"
|
||||
if [ -z "$(indent --version 2>&1 | grep GNU)" ]; then
|
||||
echo "Skipping C indentation checks, as GNU indent is not installed."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
badFiles=()
|
||||
for f in "${files[@]}"; do
|
||||
orig=$(mktemp)
|
||||
formatted=$(mktemp)
|
||||
# we use "git show" here to validate that what's committed is formatted
|
||||
git show "$VALIDATE_HEAD:$f" > ${orig}
|
||||
${INDENT} ${orig} -o ${formatted}
|
||||
if [ "$(diff -u ${orig} ${formatted})" ]; then
|
||||
badFiles+=("$f")
|
||||
fi
|
||||
rm -f ${orig} ${formatted}
|
||||
done
|
||||
|
||||
if [ ${#badFiles[@]} -eq 0 ]; then
|
||||
echo 'Congratulations! All C source files are properly formatted.'
|
||||
else
|
||||
{
|
||||
echo "These files are not properly formatted:"
|
||||
for f in "${badFiles[@]}"; do
|
||||
echo " - $f"
|
||||
done
|
||||
echo
|
||||
echo "Please reformat the above files using \"${INDENT}\" and commit the result."
|
||||
echo
|
||||
} >&2
|
||||
false
|
||||
fi
|
30
vendor/github.com/opencontainers/runc/script/validate-gofmt
generated
vendored
Executable file
30
vendor/github.com/opencontainers/runc/script/validate-gofmt
generated
vendored
Executable file
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
|
||||
source "$(dirname "$BASH_SOURCE")/.validate"
|
||||
|
||||
IFS=$'\n'
|
||||
files=($(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true))
|
||||
unset IFS
|
||||
|
||||
badFiles=()
|
||||
for f in "${files[@]}"; do
|
||||
# we use "git show" here to validate that what's committed is formatted
|
||||
if [ "$(git show "$VALIDATE_HEAD:$f" | gofmt -s -l)" ]; then
|
||||
badFiles+=("$f")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#badFiles[@]} -eq 0 ]; then
|
||||
echo 'Congratulations! All Go source files are properly formatted.'
|
||||
else
|
||||
{
|
||||
echo "These files are not properly gofmt'd:"
|
||||
for f in "${badFiles[@]}"; do
|
||||
echo " - $f"
|
||||
done
|
||||
echo
|
||||
echo 'Please reformat the above files using "gofmt -s -w" and commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
false
|
||||
fi
|
1
vendor/github.com/opencontainers/runc/signalmap.go
generated
vendored
1
vendor/github.com/opencontainers/runc/signalmap.go
generated
vendored
|
@ -37,7 +37,6 @@ var signalMap = map[string]syscall.Signal{
|
|||
"TSTP": unix.SIGTSTP,
|
||||
"TTIN": unix.SIGTTIN,
|
||||
"TTOU": unix.SIGTTOU,
|
||||
"UNUSED": unix.SIGUNUSED,
|
||||
"URG": unix.SIGURG,
|
||||
"USR1": unix.SIGUSR1,
|
||||
"USR2": unix.SIGUSR2,
|
||||
|
|
2
vendor/github.com/opencontainers/runc/signals.go
generated
vendored
2
vendor/github.com/opencontainers/runc/signals.go
generated
vendored
|
@ -29,7 +29,7 @@ func newSignalHandler(enableSubreaper bool, notifySocket *notifySocket) *signalH
|
|||
}
|
||||
}
|
||||
// ensure that we have a large buffer size so that we do not miss any signals
|
||||
// incase we are not processing them fast enough.
|
||||
// in case we are not processing them fast enough.
|
||||
s := make(chan os.Signal, signalBufferSize)
|
||||
// handle all signals for the process.
|
||||
signal.Notify(s)
|
||||
|
|
83
vendor/github.com/opencontainers/runc/tests/integration/README.md
generated
vendored
Normal file
83
vendor/github.com/opencontainers/runc/tests/integration/README.md
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
# runc Integration Tests
|
||||
|
||||
Integration tests provide end-to-end testing of runc.
|
||||
|
||||
Note that integration tests do **not** replace unit tests.
|
||||
|
||||
As a rule of thumb, code should be tested thoroughly with unit tests.
|
||||
Integration tests on the other hand are meant to test a specific feature end
|
||||
to end.
|
||||
|
||||
Integration tests are written in *bash* using the
|
||||
[bats](https://github.com/sstephenson/bats) framework.
|
||||
|
||||
## Running integration tests
|
||||
|
||||
The easiest way to run integration tests is with Docker:
|
||||
```
|
||||
$ make integration
|
||||
```
|
||||
Alternatively, you can run integration tests directly on your host through make:
|
||||
```
|
||||
$ sudo make localintegration
|
||||
```
|
||||
Or you can just run them directly using bats
|
||||
```
|
||||
$ sudo bats tests/integration
|
||||
```
|
||||
To run a single test bucket:
|
||||
```
|
||||
$ make integration TESTPATH="/checkpoint.bats"
|
||||
```
|
||||
|
||||
|
||||
To run them on your host, you will need to setup a development environment plus
|
||||
[bats](https://github.com/sstephenson/bats#installing-bats-from-source)
|
||||
For example:
|
||||
```
|
||||
$ cd ~/go/src/github.com
|
||||
$ git clone https://github.com/sstephenson/bats.git
|
||||
$ cd bats
|
||||
$ ./install.sh /usr/local
|
||||
```
|
||||
|
||||
> **Note**: There are known issues running the integration tests using
|
||||
> **devicemapper** as a storage driver, make sure that your docker daemon
|
||||
> is using **aufs** if you want to successfully run the integration tests.
|
||||
|
||||
## Writing integration tests
|
||||
|
||||
[helper functions]
|
||||
(https://github.com/opencontainers/runc/blob/master/test/integration/helpers.bash)
|
||||
are provided in order to facilitate writing tests.
|
||||
|
||||
```sh
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# This will load the helpers.
|
||||
load helpers
|
||||
|
||||
# setup is called at the beginning of every test.
|
||||
function setup() {
|
||||
# see functions teardown_hello and setup_hello in helpers.bash, used to
|
||||
# create a pristine environment for running your tests
|
||||
teardown_hello
|
||||
setup_hello
|
||||
}
|
||||
|
||||
# teardown is called at the end of every test.
|
||||
function teardown() {
|
||||
teardown_hello
|
||||
}
|
||||
|
||||
@test "this is a simple test" {
|
||||
runc run containerid
|
||||
# "The runc macro" automatically populates $status, $output and $lines.
|
||||
# Please refer to bats documentation to find out more.
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check expected output
|
||||
[[ "${output}" == *"Hello"* ]]
|
||||
}
|
||||
|
||||
```
|
127
vendor/github.com/opencontainers/runc/tests/integration/cgroups.bats
generated
vendored
Normal file
127
vendor/github.com/opencontainers/runc/tests/integration/cgroups.bats
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function teardown() {
|
||||
rm -f $BATS_TMPDIR/runc-cgroups-integration-test.json
|
||||
teardown_running_container test_cgroups_kmem
|
||||
teardown_running_container test_cgroups_permissions
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
function setup() {
|
||||
teardown
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function check_cgroup_value() {
|
||||
cgroup=$1
|
||||
source=$2
|
||||
expected=$3
|
||||
|
||||
current=$(cat $cgroup/$source)
|
||||
echo $cgroup/$source
|
||||
echo "current" $current "!?" "$expected"
|
||||
[ "$current" -eq "$expected" ]
|
||||
}
|
||||
|
||||
@test "runc update --kernel-memory (initialized)" {
|
||||
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
|
||||
requires cgroups_kmem
|
||||
|
||||
set_cgroups_path "$BUSYBOX_BUNDLE"
|
||||
|
||||
# Set some initial known values
|
||||
DATA=$(cat <<-EOF
|
||||
"memory": {
|
||||
"kernel": 16777216
|
||||
},
|
||||
EOF
|
||||
)
|
||||
DATA=$(echo ${DATA} | sed 's/\n/\\n/g')
|
||||
sed -i "s/\(\"resources\": {\)/\1\n${DATA}/" ${BUSYBOX_BUNDLE}/config.json
|
||||
|
||||
# run a detached busybox to work with
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_kmem
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# update kernel memory limit
|
||||
runc update test_cgroups_kmem --kernel-memory 50331648
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check the value
|
||||
check_cgroup_value $CGROUP_MEMORY "memory.kmem.limit_in_bytes" 50331648
|
||||
}
|
||||
|
||||
@test "runc update --kernel-memory (uninitialized)" {
|
||||
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
|
||||
requires cgroups_kmem
|
||||
|
||||
set_cgroups_path "$BUSYBOX_BUNDLE"
|
||||
|
||||
# run a detached busybox to work with
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_kmem
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# update kernel memory limit
|
||||
runc update test_cgroups_kmem --kernel-memory 50331648
|
||||
# Since kernel 4.6, we can update kernel memory without initialization
|
||||
# because it's accounted by default.
|
||||
if [ "$KERNEL_MAJOR" -lt 4 ] || [ "$KERNEL_MAJOR" -eq 4 -a "$KERNEL_MINOR" -le 5 ]; then
|
||||
[ ! "$status" -eq 0 ]
|
||||
else
|
||||
[ "$status" -eq 0 ]
|
||||
check_cgroup_value $CGROUP_MEMORY "memory.kmem.limit_in_bytes" 50331648
|
||||
fi
|
||||
}
|
||||
|
||||
@test "runc create (no limits + no cgrouppath + no permission) succeeds" {
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_permissions
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "runc create (rootless + no limits + cgrouppath + no permission) fails with permission error" {
|
||||
requires rootless
|
||||
requires rootless_no_cgroup
|
||||
|
||||
set_cgroups_path "$BUSYBOX_BUNDLE"
|
||||
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_permissions
|
||||
[ "$status" -eq 1 ]
|
||||
[[ ${lines[1]} == *"permission denied"* ]]
|
||||
}
|
||||
|
||||
@test "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error" {
|
||||
requires rootless
|
||||
requires rootless_no_cgroup
|
||||
|
||||
set_resources_limit "$BUSYBOX_BUNDLE"
|
||||
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_permissions
|
||||
[ "$status" -eq 1 ]
|
||||
[[ ${lines[1]} == *"cannot set pids limit: container could not join or create cgroup"* ]]
|
||||
}
|
||||
|
||||
@test "runc create (limits + cgrouppath + permission on the cgroup dir) succeeds" {
|
||||
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
|
||||
|
||||
set_cgroups_path "$BUSYBOX_BUNDLE"
|
||||
set_resources_limit "$BUSYBOX_BUNDLE"
|
||||
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_permissions
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "runc exec (limits + cgrouppath + permission on the cgroup dir) succeeds" {
|
||||
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_cgroup
|
||||
|
||||
set_cgroups_path "$BUSYBOX_BUNDLE"
|
||||
set_resources_limit "$BUSYBOX_BUNDLE"
|
||||
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_cgroups_permissions
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec test_cgroups_permissions echo "cgroups_exec"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} == *"cgroups_exec"* ]]
|
||||
}
|
297
vendor/github.com/opencontainers/runc/tests/integration/checkpoint.bats
generated
vendored
Normal file
297
vendor/github.com/opencontainers/runc/tests/integration/checkpoint.bats
generated
vendored
Normal file
|
@ -0,0 +1,297 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "checkpoint and restore" {
|
||||
# XXX: currently criu require root containers.
|
||||
requires criu root
|
||||
|
||||
# criu does not work with external terminals so..
|
||||
# setting terminal and root:readonly: to false
|
||||
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
|
||||
for i in `seq 2`; do
|
||||
# checkpoint the running container
|
||||
runc --criu "$CRIU" checkpoint --work-path ./work-dir test_busybox
|
||||
ret=$?
|
||||
# if you are having problems getting criu to work uncomment the following dump:
|
||||
#cat /run/opencontainer/containers/test_busybox/criu.work/dump.log
|
||||
cat ./work-dir/dump.log | grep -B 5 Error || true
|
||||
[ "$ret" -eq 0 ]
|
||||
|
||||
# after checkpoint busybox is no longer running
|
||||
runc state test_busybox
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
# restore from checkpoint
|
||||
runc --criu "$CRIU" restore -d --work-path ./work-dir --console-socket $CONSOLE_SOCKET test_busybox
|
||||
ret=$?
|
||||
cat ./work-dir/restore.log | grep -B 5 Error || true
|
||||
[ "$ret" -eq 0 ]
|
||||
|
||||
# busybox should be back up and running
|
||||
testcontainer test_busybox running
|
||||
done
|
||||
}
|
||||
|
||||
@test "checkpoint --pre-dump and restore" {
|
||||
# XXX: currently criu require root containers.
|
||||
requires criu root
|
||||
|
||||
sed -i 's;"terminal": true;"terminal": false;' config.json
|
||||
sed -i 's;"readonly": true;"readonly": false;' config.json
|
||||
sed -i 's/"sh"/"sh","-c","for i in `seq 10`; do read xxx || continue; echo ponG $xxx; done"/' config.json
|
||||
|
||||
# The following code creates pipes for stdin and stdout.
|
||||
# CRIU can't handle fifo-s, so we need all these tricks.
|
||||
fifo=`mktemp -u /tmp/runc-fifo-XXXXXX`
|
||||
mkfifo $fifo
|
||||
|
||||
# stdout
|
||||
cat $fifo | cat $fifo &
|
||||
pid=$!
|
||||
exec 50</proc/$pid/fd/0
|
||||
exec 51>/proc/$pid/fd/0
|
||||
|
||||
# stdin
|
||||
cat $fifo | cat $fifo &
|
||||
pid=$!
|
||||
exec 60</proc/$pid/fd/0
|
||||
exec 61>/proc/$pid/fd/0
|
||||
|
||||
echo -n > $fifo
|
||||
unlink $fifo
|
||||
|
||||
# run busybox (not detached)
|
||||
__runc run -d test_busybox <&60 >&51 2>&51
|
||||
[ $? -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
|
||||
#test checkpoint pre-dump
|
||||
mkdir parent-dir
|
||||
runc --criu "$CRIU" checkpoint --pre-dump --image-path ./parent-dir test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# busybox should still be running
|
||||
runc state test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"running"* ]]
|
||||
|
||||
# checkpoint the running container
|
||||
mkdir image-dir
|
||||
mkdir work-dir
|
||||
runc --criu "$CRIU" checkpoint --parent-path ./parent-dir --work-path ./work-dir --image-path ./image-dir test_busybox
|
||||
cat ./work-dir/dump.log | grep -B 5 Error || true
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# after checkpoint busybox is no longer running
|
||||
runc state test_busybox
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
# restore from checkpoint
|
||||
__runc --criu "$CRIU" restore -d --work-path ./work-dir --image-path ./image-dir test_busybox <&60 >&51 2>&51
|
||||
ret=$?
|
||||
cat ./work-dir/restore.log | grep -B 5 Error || true
|
||||
[ $ret -eq 0 ]
|
||||
|
||||
# busybox should be back up and running
|
||||
testcontainer test_busybox running
|
||||
|
||||
runc exec --cwd /bin test_busybox echo ok
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${output} == "ok" ]]
|
||||
|
||||
echo Ping >&61
|
||||
exec 61>&-
|
||||
exec 51>&-
|
||||
run cat <&50
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"ponG Ping"* ]]
|
||||
}
|
||||
|
||||
@test "checkpoint --lazy-pages and restore" {
|
||||
# XXX: currently criu require root containers.
|
||||
requires criu root
|
||||
|
||||
# check if lazy-pages is supported
|
||||
run ${CRIU} check --feature uffd-noncoop
|
||||
if [ "$status" -eq 1 ]; then
|
||||
# this criu does not support lazy migration; skip the test
|
||||
skip "this criu does not support lazy migration"
|
||||
fi
|
||||
|
||||
sed -i 's;"terminal": true;"terminal": false;' config.json
|
||||
sed -i 's;"readonly": true;"readonly": false;' config.json
|
||||
sed -i 's/"sh"/"sh","-c","for i in `seq 10`; do read xxx || continue; echo ponG $xxx; done"/' config.json
|
||||
|
||||
# The following code creates pipes for stdin and stdout.
|
||||
# CRIU can't handle fifo-s, so we need all these tricks.
|
||||
fifo=`mktemp -u /tmp/runc-fifo-XXXXXX`
|
||||
mkfifo $fifo
|
||||
|
||||
# For lazy migration we need to know when CRIU is ready to serve
|
||||
# the memory pages via TCP.
|
||||
lazy_pipe=`mktemp -u /tmp/lazy-pipe-XXXXXX`
|
||||
mkfifo $lazy_pipe
|
||||
|
||||
# TCP port for lazy migration
|
||||
port=27277
|
||||
|
||||
# stdout
|
||||
cat $fifo | cat $fifo &
|
||||
pid=$!
|
||||
exec 50</proc/$pid/fd/0
|
||||
exec 51>/proc/$pid/fd/0
|
||||
|
||||
# stdin
|
||||
cat $fifo | cat $fifo &
|
||||
pid=$!
|
||||
exec 60</proc/$pid/fd/0
|
||||
exec 61>/proc/$pid/fd/0
|
||||
|
||||
echo -n > $fifo
|
||||
unlink $fifo
|
||||
|
||||
# run busybox
|
||||
__runc run -d test_busybox <&60 >&51 2>&51
|
||||
[ $? -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
|
||||
# checkpoint the running container
|
||||
mkdir image-dir
|
||||
mkdir work-dir
|
||||
# Double fork taken from helpers.bats
|
||||
# We need to start 'runc checkpoint --lazy-pages' in the background,
|
||||
# so we double fork in the shell.
|
||||
(runc --criu "$CRIU" checkpoint --lazy-pages --page-server 0.0.0.0:${port} --status-fd ${lazy_pipe} --work-path ./work-dir --image-path ./image-dir test_busybox & ) &
|
||||
# Sleeping here. This is ugly, but not sure how else to handle it.
|
||||
# The return code of the in the background running runc is needed, if
|
||||
# there is some basic error. If the lazy migration is ready can
|
||||
# be handled by $lazy_pipe. Which probably will always be ready
|
||||
# after sleeping two seconds.
|
||||
sleep 2
|
||||
# Check if inventory.img was written
|
||||
[ -e image-dir/inventory.img ]
|
||||
# If the inventory.img exists criu checkpointed some things, let's see
|
||||
# if there were other errors in the log file.
|
||||
run grep -B 5 Error ./work-dir/dump.log -q
|
||||
[ "$status" -eq 1 ]
|
||||
|
||||
# This will block until CRIU is ready to serve memory pages
|
||||
cat $lazy_pipe
|
||||
[ "$status" -eq 1 ]
|
||||
|
||||
unlink $lazy_pipe
|
||||
|
||||
# Double fork taken from helpers.bats
|
||||
# We need to start 'criu lazy-pages' in the background,
|
||||
# so we double fork in the shell.
|
||||
# Start CRIU in lazy-daemon mode
|
||||
$(${CRIU} lazy-pages --page-server --address 127.0.0.1 --port ${port} -D image-dir &) &
|
||||
|
||||
# Restore lazily from checkpoint.
|
||||
# The restored container needs a different name as the checkpointed
|
||||
# container is not yet destroyed. It is only destroyed at that point
|
||||
# in time when the last page is lazily transferred to the destination.
|
||||
# Killing the CRIU on the checkpoint side will let the container
|
||||
# continue to run if the migration failed at some point.
|
||||
__runc --criu "$CRIU" restore -d --work-path ./image-dir --image-path ./image-dir --lazy-pages test_busybox_restore <&60 >&51 2>&51
|
||||
ret=$?
|
||||
[ $ret -eq 0 ]
|
||||
run grep -B 5 Error ./work-dir/dump.log -q
|
||||
[ "$status" -eq 1 ]
|
||||
|
||||
# busybox should be back up and running
|
||||
testcontainer test_busybox_restore running
|
||||
|
||||
runc exec --cwd /bin test_busybox_restore echo ok
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${output} == "ok" ]]
|
||||
|
||||
echo Ping >&61
|
||||
exec 61>&-
|
||||
exec 51>&-
|
||||
run cat <&50
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"ponG Ping"* ]]
|
||||
}
|
||||
|
||||
@test "checkpoint and restore in external network namespace" {
|
||||
# XXX: currently criu require root containers.
|
||||
requires criu root
|
||||
|
||||
# check if external_net_ns is supported; only with criu 3.10++
|
||||
run ${CRIU} check --feature external_net_ns
|
||||
if [ "$status" -eq 1 ]; then
|
||||
# this criu does not support external_net_ns; skip the test
|
||||
skip "this criu does not support external network namespaces"
|
||||
fi
|
||||
|
||||
# create a temporary name for the test network namespace
|
||||
tmp=`mktemp`
|
||||
rm -f $tmp
|
||||
ns_name=`basename $tmp`
|
||||
# create network namespace
|
||||
ip netns add $ns_name
|
||||
ns_path=`ip netns add $ns_name 2>&1 | sed -e 's/.*"\(.*\)".*/\1/'`
|
||||
|
||||
ns_inode=`ls -iL $ns_path | awk '{ print $1 }'`
|
||||
|
||||
# not necessary with criu 3.10 any more
|
||||
sed -i 's;"readonly": true;"readonly": false;' config.json
|
||||
# tell runc which network namespace to use
|
||||
sed -i "s;\"type\": \"network\";\"type\": \"network\",\"path\": \"$ns_path\";" config.json
|
||||
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
|
||||
for i in `seq 2`; do
|
||||
# checkpoint the running container; this automatically tells CRIU to
|
||||
# handle the network namespace defined in config.json as an external
|
||||
runc --criu "$CRIU" checkpoint --work-path ./work-dir test_busybox
|
||||
ret=$?
|
||||
# if you are having problems getting criu to work uncomment the following dump:
|
||||
#cat /run/opencontainer/containers/test_busybox/criu.work/dump.log
|
||||
cat ./work-dir/dump.log | grep -B 5 Error || true
|
||||
[ "$ret" -eq 0 ]
|
||||
|
||||
# after checkpoint busybox is no longer running
|
||||
runc state test_busybox
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
# restore from checkpoint; this should restore the container into the existing network namespace
|
||||
runc --criu "$CRIU" restore -d --work-path ./work-dir --console-socket $CONSOLE_SOCKET test_busybox
|
||||
ret=$?
|
||||
cat ./work-dir/restore.log | grep -B 5 Error || true
|
||||
[ "$ret" -eq 0 ]
|
||||
|
||||
# busybox should be back up and running
|
||||
testcontainer test_busybox running
|
||||
|
||||
# container should be running in same network namespace as before
|
||||
pid=`__runc state test_busybox | jq '.pid'`
|
||||
ns_inode_new=`readlink /proc/$pid/ns/net | sed -e 's/.*\[\(.*\)\]/\1/'`
|
||||
echo "old network namespace inode $ns_inode"
|
||||
echo "new network namespace inode $ns_inode_new"
|
||||
[ "$ns_inode" -eq "$ns_inode_new" ]
|
||||
done
|
||||
ip netns del $ns_name
|
||||
}
|
||||
|
89
vendor/github.com/opencontainers/runc/tests/integration/create.bats
generated
vendored
Normal file
89
vendor/github.com/opencontainers/runc/tests/integration/create.bats
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "runc create" {
|
||||
runc create --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
# start the command
|
||||
runc start test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
}
|
||||
|
||||
@test "runc create exec" {
|
||||
runc create --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
runc exec test_busybox true
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
# start the command
|
||||
runc start test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
}
|
||||
|
||||
@test "runc create --pid-file" {
|
||||
runc create --pid-file pid.txt --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
# check pid.txt was generated
|
||||
[ -e pid.txt ]
|
||||
|
||||
run cat pid.txt
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} == $(__runc state test_busybox | jq '.pid') ]]
|
||||
|
||||
# start the command
|
||||
runc start test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
}
|
||||
|
||||
@test "runc create --pid-file with new CWD" {
|
||||
# create pid_file directory as the CWD
|
||||
run mkdir pid_file
|
||||
[ "$status" -eq 0 ]
|
||||
run cd pid_file
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc create --pid-file pid.txt -b $BUSYBOX_BUNDLE --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
# check pid.txt was generated
|
||||
[ -e pid.txt ]
|
||||
|
||||
run cat pid.txt
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} == $(__runc state test_busybox | jq '.pid') ]]
|
||||
|
||||
# start the command
|
||||
runc start test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
}
|
70
vendor/github.com/opencontainers/runc/tests/integration/debug.bats
generated
vendored
Normal file
70
vendor/github.com/opencontainers/runc/tests/integration/debug.bats
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_hello
|
||||
setup_hello
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_hello
|
||||
}
|
||||
|
||||
@test "global --debug" {
|
||||
# run hello-world
|
||||
runc --debug run test_hello
|
||||
echo "${output}"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "global --debug to --log" {
|
||||
# run hello-world
|
||||
runc --log log.out --debug run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check output does not include debug info
|
||||
[[ "${output}" != *"level=debug"* ]]
|
||||
|
||||
# check log.out was generated
|
||||
[ -e log.out ]
|
||||
|
||||
# check expected debug output was sent to log.out
|
||||
run cat log.out
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"level=debug"* ]]
|
||||
}
|
||||
|
||||
@test "global --debug to --log --log-format 'text'" {
|
||||
# run hello-world
|
||||
runc --log log.out --log-format "text" --debug run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check output does not include debug info
|
||||
[[ "${output}" != *"level=debug"* ]]
|
||||
|
||||
# check log.out was generated
|
||||
[ -e log.out ]
|
||||
|
||||
# check expected debug output was sent to log.out
|
||||
run cat log.out
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"level=debug"* ]]
|
||||
}
|
||||
|
||||
@test "global --debug to --log --log-format 'json'" {
|
||||
# run hello-world
|
||||
runc --log log.out --log-format "json" --debug run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check output does not include debug info
|
||||
[[ "${output}" != *"level=debug"* ]]
|
||||
|
||||
# check log.out was generated
|
||||
[ -e log.out ]
|
||||
|
||||
# check expected debug output was sent to log.out
|
||||
run cat log.out
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *'"level":"debug"'* ]]
|
||||
}
|
53
vendor/github.com/opencontainers/runc/tests/integration/delete.bats
generated
vendored
Normal file
53
vendor/github.com/opencontainers/runc/tests/integration/delete.bats
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "runc delete" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
testcontainer test_busybox running
|
||||
|
||||
runc kill test_busybox KILL
|
||||
[ "$status" -eq 0 ]
|
||||
# wait for busybox to be in the destroyed state
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'stopped'"
|
||||
|
||||
# delete test_busybox
|
||||
runc delete test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc state test_busybox
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "runc delete --force" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
testcontainer test_busybox running
|
||||
|
||||
# force delete test_busybox
|
||||
runc delete --force test_busybox
|
||||
|
||||
runc state test_busybox
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "runc delete --force ignore not exist" {
|
||||
runc delete --force notexists
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
109
vendor/github.com/opencontainers/runc/tests/integration/events.bats
generated
vendored
Normal file
109
vendor/github.com/opencontainers/runc/tests/integration/events.bats
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "events --stats" {
|
||||
# XXX: currently cgroups require root containers.
|
||||
requires root
|
||||
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# generate stats
|
||||
runc events --stats test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[0]}" == [\{]"\"type\""[:]"\"stats\""[,]"\"id\""[:]"\"test_busybox\""[,]* ]]
|
||||
[[ "${lines[0]}" == *"data"* ]]
|
||||
}
|
||||
|
||||
@test "events --interval default " {
|
||||
# XXX: currently cgroups require root containers.
|
||||
requires root
|
||||
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# spawn two sub processes (shells)
|
||||
# the first sub process is an event logger that sends stats events to events.log
|
||||
# the second sub process waits for an event that includes test_busybox then
|
||||
# kills the test_busybox container which causes the event logger to exit
|
||||
(__runc events test_busybox > events.log) &
|
||||
(
|
||||
retry 10 1 eval "grep -q 'test_busybox' events.log"
|
||||
teardown_running_container test_busybox
|
||||
) &
|
||||
wait # wait for the above sub shells to finish
|
||||
|
||||
[ -e events.log ]
|
||||
|
||||
run cat events.log
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[0]}" == [\{]"\"type\""[:]"\"stats\""[,]"\"id\""[:]"\"test_busybox\""[,]* ]]
|
||||
[[ "${lines[0]}" == *"data"* ]]
|
||||
}
|
||||
|
||||
@test "events --interval 1s " {
|
||||
# XXX: currently cgroups require root containers.
|
||||
requires root
|
||||
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# spawn two sub processes (shells)
|
||||
# the first sub process is an event logger that sends stats events to events.log once a second
|
||||
# the second sub process tries 3 times for an event that incudes test_busybox
|
||||
# pausing 1s between each attempt then kills the test_busybox container which
|
||||
# causes the event logger to exit
|
||||
(__runc events --interval 1s test_busybox > events.log) &
|
||||
(
|
||||
retry 3 1 eval "grep -q 'test_busybox' events.log"
|
||||
teardown_running_container test_busybox
|
||||
) &
|
||||
wait # wait for the above sub shells to finish
|
||||
|
||||
[ -e events.log ]
|
||||
|
||||
run eval "grep -q 'test_busybox' events.log"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "events --interval 100ms " {
|
||||
# XXX: currently cgroups require root containers.
|
||||
requires root
|
||||
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
#prove there is no carry over of events.log from a prior test
|
||||
[ ! -e events.log ]
|
||||
|
||||
# spawn two sub processes (shells)
|
||||
# the first sub process is an event logger that sends stats events to events.log once every 100ms
|
||||
# the second sub process tries 3 times for an event that incudes test_busybox
|
||||
# pausing 100s between each attempt then kills the test_busybox container which
|
||||
# causes the event logger to exit
|
||||
(__runc events --interval 100ms test_busybox > events.log) &
|
||||
(
|
||||
retry 3 0.100 eval "grep -q 'test_busybox' events.log"
|
||||
teardown_running_container test_busybox
|
||||
) &
|
||||
wait # wait for the above sub shells to finish
|
||||
|
||||
[ -e events.log ]
|
||||
|
||||
run eval "grep -q 'test_busybox' events.log"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
129
vendor/github.com/opencontainers/runc/tests/integration/exec.bats
generated
vendored
Normal file
129
vendor/github.com/opencontainers/runc/tests/integration/exec.bats
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "runc exec" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec test_busybox echo Hello from exec
|
||||
[ "$status" -eq 0 ]
|
||||
echo text echoed = "'""${output}""'"
|
||||
[[ "${output}" == *"Hello from exec"* ]]
|
||||
}
|
||||
|
||||
@test "runc exec --pid-file" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec --pid-file pid.txt test_busybox echo Hello from exec
|
||||
[ "$status" -eq 0 ]
|
||||
echo text echoed = "'""${output}""'"
|
||||
[[ "${output}" == *"Hello from exec"* ]]
|
||||
|
||||
# check pid.txt was generated
|
||||
[ -e pid.txt ]
|
||||
|
||||
run cat pid.txt
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ [0-9]+ ]]
|
||||
[[ ${lines[0]} != $(__runc state test_busybox | jq '.pid') ]]
|
||||
}
|
||||
|
||||
@test "runc exec --pid-file with new CWD" {
|
||||
# create pid_file directory as the CWD
|
||||
run mkdir pid_file
|
||||
[ "$status" -eq 0 ]
|
||||
run cd pid_file
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# run busybox detached
|
||||
runc run -d -b $BUSYBOX_BUNDLE --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec --pid-file pid.txt test_busybox echo Hello from exec
|
||||
[ "$status" -eq 0 ]
|
||||
echo text echoed = "'""${output}""'"
|
||||
[[ "${output}" == *"Hello from exec"* ]]
|
||||
|
||||
# check pid.txt was generated
|
||||
[ -e pid.txt ]
|
||||
|
||||
run cat pid.txt
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ [0-9]+ ]]
|
||||
[[ ${lines[0]} != $(__runc state test_busybox | jq '.pid') ]]
|
||||
}
|
||||
|
||||
@test "runc exec ls -la" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec test_busybox ls -la
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} == *"total"* ]]
|
||||
[[ ${lines[1]} == *"."* ]]
|
||||
[[ ${lines[2]} == *".."* ]]
|
||||
}
|
||||
|
||||
@test "runc exec ls -la with --cwd" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec --cwd /bin test_busybox pwd
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${output} == "/bin"* ]]
|
||||
}
|
||||
|
||||
@test "runc exec --env" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec --env RUNC_EXEC_TEST=true test_busybox env
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
[[ ${output} == *"RUNC_EXEC_TEST=true"* ]]
|
||||
}
|
||||
|
||||
@test "runc exec --user" {
|
||||
# --user can't work in rootless containers that don't have idmap.
|
||||
[[ "$ROOTLESS" -ne 0 ]] && requires rootless_idmap
|
||||
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec --user 1000:1000 test_busybox id
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
[[ "${output}" == "uid=1000 gid=1000"* ]]
|
||||
}
|
||||
|
||||
@test "runc exec --additional-gids" {
|
||||
requires root
|
||||
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
wait_for_container 15 1 test_busybox
|
||||
|
||||
runc exec --user 1000:1000 --additional-gids 100 --additional-gids 99 test_busybox id
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
[[ ${output} == "uid=1000 gid=1000 groups=99(nogroup),100(users)" ]]
|
||||
}
|
87
vendor/github.com/opencontainers/runc/tests/integration/help.bats
generated
vendored
Normal file
87
vendor/github.com/opencontainers/runc/tests/integration/help.bats
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
@test "runc -h" {
|
||||
runc -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ NAME:+ ]]
|
||||
[[ ${lines[1]} =~ runc\ '-'\ Open\ Container\ Initiative\ runtime+ ]]
|
||||
|
||||
runc --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ NAME:+ ]]
|
||||
[[ ${lines[1]} =~ runc\ '-'\ Open\ Container\ Initiative\ runtime+ ]]
|
||||
}
|
||||
|
||||
@test "runc command -h" {
|
||||
runc checkpoint -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ checkpoint+ ]]
|
||||
|
||||
runc delete -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ delete+ ]]
|
||||
|
||||
runc events -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ events+ ]]
|
||||
|
||||
runc exec -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ exec+ ]]
|
||||
|
||||
runc kill -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ kill+ ]]
|
||||
|
||||
runc list -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ NAME:+ ]]
|
||||
[[ ${lines[1]} =~ runc\ list+ ]]
|
||||
|
||||
runc list --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ NAME:+ ]]
|
||||
[[ ${lines[1]} =~ runc\ list+ ]]
|
||||
|
||||
runc pause -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ pause+ ]]
|
||||
|
||||
runc restore -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ restore+ ]]
|
||||
|
||||
runc resume -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ resume+ ]]
|
||||
|
||||
# We don't use runc_spec here, because we're just testing the help page.
|
||||
runc spec -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ spec+ ]]
|
||||
|
||||
runc start -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ start+ ]]
|
||||
|
||||
runc run -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ run+ ]]
|
||||
|
||||
runc state -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ state+ ]]
|
||||
|
||||
runc update -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ update+ ]]
|
||||
|
||||
}
|
||||
|
||||
@test "runc foo -h" {
|
||||
runc foo -h
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "${output}" == *"No help topic for 'foo'"* ]]
|
||||
}
|
330
vendor/github.com/opencontainers/runc/tests/integration/helpers.bash
generated
vendored
Normal file
330
vendor/github.com/opencontainers/runc/tests/integration/helpers.bash
generated
vendored
Normal file
|
@ -0,0 +1,330 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Root directory of integration tests.
|
||||
INTEGRATION_ROOT=$(dirname "$(readlink -f "$BASH_SOURCE")")
|
||||
|
||||
. ${INTEGRATION_ROOT}/multi-arch.bash
|
||||
|
||||
RUNC="${INTEGRATION_ROOT}/../../runc"
|
||||
RECVTTY="${INTEGRATION_ROOT}/../../contrib/cmd/recvtty/recvtty"
|
||||
GOPATH="$(mktemp -d --tmpdir runc-integration-gopath.XXXXXX)"
|
||||
|
||||
# Test data path.
|
||||
TESTDATA="${INTEGRATION_ROOT}/testdata"
|
||||
|
||||
# Busybox image
|
||||
BUSYBOX_IMAGE="$BATS_TMPDIR/busybox.tar"
|
||||
BUSYBOX_BUNDLE="$BATS_TMPDIR/busyboxtest"
|
||||
|
||||
# hello-world in tar format
|
||||
HELLO_FILE=`get_hello`
|
||||
HELLO_IMAGE="$TESTDATA/$HELLO_FILE"
|
||||
HELLO_BUNDLE="$BATS_TMPDIR/hello-world"
|
||||
|
||||
# CRIU PATH
|
||||
CRIU="$(which criu || true)"
|
||||
|
||||
# Kernel version
|
||||
KERNEL_VERSION="$(uname -r)"
|
||||
KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
|
||||
KERNEL_MINOR="${KERNEL_VERSION#$KERNEL_MAJOR.}"
|
||||
KERNEL_MINOR="${KERNEL_MINOR%%.*}"
|
||||
|
||||
# Root state path.
|
||||
ROOT=$(mktemp -d "$BATS_TMPDIR/runc.XXXXXX")
|
||||
|
||||
# Path to console socket.
|
||||
CONSOLE_SOCKET="$BATS_TMPDIR/console.sock"
|
||||
|
||||
# Cgroup paths
|
||||
CGROUP_MEMORY_BASE_PATH=$(grep "cgroup" /proc/self/mountinfo | gawk 'toupper($NF) ~ /\<MEMORY\>/ { print $5; exit }')
|
||||
CGROUP_CPU_BASE_PATH=$(grep "cgroup" /proc/self/mountinfo | gawk 'toupper($NF) ~ /\<CPU\>/ { print $5; exit }')
|
||||
CGROUPS_PATH="/runc-cgroups-integration-test/test-cgroup"
|
||||
CGROUP_MEMORY="${CGROUP_MEMORY_BASE_PATH}${CGROUPS_PATH}"
|
||||
|
||||
# CONFIG_MEMCG_KMEM support
|
||||
KMEM="${CGROUP_MEMORY_BASE_PATH}/memory.kmem.limit_in_bytes"
|
||||
RT_PERIOD="${CGROUP_CPU_BASE_PATH}/cpu.rt_period_us"
|
||||
|
||||
# Check if we're in rootless mode.
|
||||
ROOTLESS=$(id -u)
|
||||
|
||||
# Wrapper for runc.
|
||||
function runc() {
|
||||
run __runc "$@"
|
||||
|
||||
# Some debug information to make life easier. bats will only print it if the
|
||||
# test failed, in which case the output is useful.
|
||||
echo "runc $@ (status=$status):" >&2
|
||||
echo "$output" >&2
|
||||
}
|
||||
|
||||
# Raw wrapper for runc.
|
||||
function __runc() {
|
||||
"$RUNC" --log /proc/self/fd/2 --root "$ROOT" "$@"
|
||||
}
|
||||
|
||||
# Wrapper for runc spec, which takes only one argument (the bundle path).
|
||||
function runc_spec() {
|
||||
! [[ "$#" > 1 ]]
|
||||
|
||||
local args=()
|
||||
local bundle=""
|
||||
|
||||
if [ "$ROOTLESS" -ne 0 ]; then
|
||||
args+=("--rootless")
|
||||
fi
|
||||
if [ "$#" -ne 0 ]; then
|
||||
bundle="$1"
|
||||
args+=("--bundle" "$bundle")
|
||||
fi
|
||||
|
||||
runc spec "${args[@]}"
|
||||
|
||||
# Always add additional mappings if we have idmaps.
|
||||
if [[ "$ROOTLESS" -ne 0 ]] && [[ "$ROOTLESS_FEATURES" == *"idmap"* ]]; then
|
||||
runc_rootless_idmap "$bundle"
|
||||
fi
|
||||
|
||||
# Ensure config.json contains linux.resources
|
||||
if [[ "$ROOTLESS" -ne 0 ]] && [[ "$ROOTLESS_FEATURES" == *"cgroup"* ]]; then
|
||||
runc_rootless_cgroup "$bundle"
|
||||
fi
|
||||
}
|
||||
|
||||
# Shortcut to add additional uids and gids, based on the values set as part of
|
||||
# a rootless configuration.
|
||||
function runc_rootless_idmap() {
|
||||
bundle="${1:-.}"
|
||||
cat "$bundle/config.json" \
|
||||
| jq '.mounts |= map((select(.type == "devpts") | .options += ["gid=5"]) // .)' \
|
||||
| jq '.linux.uidMappings |= .+ [{"hostID": '"$ROOTLESS_UIDMAP_START"', "containerID": 1000, "size": '"$ROOTLESS_UIDMAP_LENGTH"'}]' \
|
||||
| jq '.linux.gidMappings |= .+ [{"hostID": '"$ROOTLESS_GIDMAP_START"', "containerID": 100, "size": 1}]' \
|
||||
| jq '.linux.gidMappings |= .+ [{"hostID": '"$(($ROOTLESS_GIDMAP_START+10))"', "containerID": 1, "size": 20}]' \
|
||||
| jq '.linux.gidMappings |= .+ [{"hostID": '"$(($ROOTLESS_GIDMAP_START+100))"', "containerID": 1000, "size": '"$(($ROOTLESS_GIDMAP_LENGTH-1000))"'}]' \
|
||||
>"$bundle/config.json.tmp"
|
||||
mv "$bundle/config.json"{.tmp,}
|
||||
}
|
||||
|
||||
# Shortcut to add empty resources as part of a rootless configuration.
|
||||
function runc_rootless_cgroup() {
|
||||
bundle="${1:-.}"
|
||||
cat "$bundle/config.json" \
|
||||
| jq '.linux.resources |= .+ {"memory":{},"cpu":{},"blockio":{},"pids":{}}' \
|
||||
>"$bundle/config.json.tmp"
|
||||
mv "$bundle/config.json"{.tmp,}
|
||||
}
|
||||
|
||||
# Helper function to set cgroupsPath to the value of $CGROUPS_PATH
|
||||
function set_cgroups_path() {
|
||||
bundle="${1:-.}"
|
||||
sed -i 's/\("linux": {\)/\1\n "cgroupsPath": "\/runc-cgroups-integration-test\/test-cgroup",/' "$bundle/config.json"
|
||||
}
|
||||
|
||||
# Helper function to set a resources limit
|
||||
function set_resources_limit() {
|
||||
bundle="${1:-.}"
|
||||
sed -i 's/\("linux": {\)/\1\n "resources": { "pids": { "limit": 100 } },/' "$bundle/config.json"
|
||||
}
|
||||
|
||||
# Fails the current test, providing the error given.
|
||||
function fail() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Allows a test to specify what things it requires. If the environment can't
|
||||
# support it, the test is skipped with a message.
|
||||
function requires() {
|
||||
for var in "$@"; do
|
||||
case $var in
|
||||
criu)
|
||||
if [ ! -e "$CRIU" ]; then
|
||||
skip "test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
root)
|
||||
if [ "$ROOTLESS" -ne 0 ]; then
|
||||
skip "test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
rootless)
|
||||
if [ "$ROOTLESS" -eq 0 ]; then
|
||||
skip "test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
rootless_idmap)
|
||||
if [[ "$ROOTLESS_FEATURES" != *"idmap"* ]]; then
|
||||
skip "test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
rootless_cgroup)
|
||||
if [[ "$ROOTLESS_FEATURES" != *"cgroup"* ]]; then
|
||||
skip "test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
rootless_no_cgroup)
|
||||
if [[ "$ROOTLESS_FEATURES" == *"cgroup"* ]]; then
|
||||
skip "test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
cgroups_kmem)
|
||||
if [ ! -e "$KMEM" ]; then
|
||||
skip "Test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
cgroups_rt)
|
||||
if [ ! -e "$RT_PERIOD" ]; then
|
||||
skip "Test requires ${var}"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
fail "BUG: Invalid requires ${var}."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Retry a command $1 times until it succeeds. Wait $2 seconds between retries.
|
||||
function retry() {
|
||||
local attempts=$1
|
||||
shift
|
||||
local delay=$1
|
||||
shift
|
||||
local i
|
||||
|
||||
for ((i = 0; i < attempts; i++)); do
|
||||
run "$@"
|
||||
if [[ "$status" -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep $delay
|
||||
done
|
||||
|
||||
echo "Command \"$@\" failed $attempts times. Output: $output"
|
||||
false
|
||||
}
|
||||
|
||||
# retry until the given container has state
|
||||
function wait_for_container() {
|
||||
local attempts=$1
|
||||
local delay=$2
|
||||
local cid=$3
|
||||
local i
|
||||
|
||||
for ((i = 0; i < attempts; i++)); do
|
||||
runc state $cid
|
||||
if [[ "$status" -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep $delay
|
||||
done
|
||||
|
||||
echo "runc state failed to return state $statecheck $attempts times. Output: $output"
|
||||
false
|
||||
}
|
||||
|
||||
# retry until the given container has state
|
||||
function wait_for_container_inroot() {
|
||||
local attempts=$1
|
||||
local delay=$2
|
||||
local cid=$3
|
||||
local i
|
||||
|
||||
for ((i = 0; i < attempts; i++)); do
|
||||
ROOT=$4 runc state $cid
|
||||
if [[ "$status" -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep $delay
|
||||
done
|
||||
|
||||
echo "runc state failed to return state $statecheck $attempts times. Output: $output"
|
||||
false
|
||||
}
|
||||
|
||||
function testcontainer() {
|
||||
# test state of container
|
||||
runc state $1
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"$2"* ]]
|
||||
}
|
||||
|
||||
function setup_recvtty() {
|
||||
# We need to start recvtty in the background, so we double fork in the shell.
|
||||
("$RECVTTY" --pid-file "$BATS_TMPDIR/recvtty.pid" --mode null "$CONSOLE_SOCKET" &) &
|
||||
}
|
||||
|
||||
function teardown_recvtty() {
|
||||
# When we kill recvtty, the container will also be killed.
|
||||
if [ -f "$BATS_TMPDIR/recvtty.pid" ]; then
|
||||
kill -9 $(cat "$BATS_TMPDIR/recvtty.pid")
|
||||
fi
|
||||
|
||||
# Clean up the files that might be left over.
|
||||
rm -f "$BATS_TMPDIR/recvtty.pid"
|
||||
rm -f "$CONSOLE_SOCKET"
|
||||
}
|
||||
|
||||
function setup_busybox() {
|
||||
setup_recvtty
|
||||
run mkdir "$BUSYBOX_BUNDLE"
|
||||
run mkdir "$BUSYBOX_BUNDLE"/rootfs
|
||||
if [ -e "/testdata/busybox.tar" ]; then
|
||||
BUSYBOX_IMAGE="/testdata/busybox.tar"
|
||||
fi
|
||||
if [ ! -e $BUSYBOX_IMAGE ]; then
|
||||
curl -o $BUSYBOX_IMAGE -sSL `get_busybox`
|
||||
fi
|
||||
tar --exclude './dev/*' -C "$BUSYBOX_BUNDLE"/rootfs -xf "$BUSYBOX_IMAGE"
|
||||
cd "$BUSYBOX_BUNDLE"
|
||||
runc_spec
|
||||
}
|
||||
|
||||
function setup_hello() {
|
||||
setup_recvtty
|
||||
run mkdir "$HELLO_BUNDLE"
|
||||
run mkdir "$HELLO_BUNDLE"/rootfs
|
||||
tar --exclude './dev/*' -C "$HELLO_BUNDLE"/rootfs -xf "$HELLO_IMAGE"
|
||||
cd "$HELLO_BUNDLE"
|
||||
runc_spec
|
||||
sed -i 's;"sh";"/hello";' config.json
|
||||
}
|
||||
|
||||
function teardown_running_container() {
|
||||
runc list
|
||||
# $1 should be a container name such as "test_busybox"
|
||||
# here we detect "test_busybox "(with one extra blank) to avoid conflict prefix
|
||||
# e.g. "test_busybox" and "test_busybox_update"
|
||||
if [[ "${output}" == *"$1 "* ]]; then
|
||||
runc kill $1 KILL
|
||||
retry 10 1 eval "__runc state '$1' | grep -q 'stopped'"
|
||||
runc delete $1
|
||||
fi
|
||||
}
|
||||
|
||||
function teardown_running_container_inroot() {
|
||||
ROOT=$2 runc list
|
||||
# $1 should be a container name such as "test_busybox"
|
||||
# here we detect "test_busybox "(with one extra blank) to avoid conflict prefix
|
||||
# e.g. "test_busybox" and "test_busybox_update"
|
||||
if [[ "${output}" == *"$1 "* ]]; then
|
||||
ROOT=$2 runc kill $1 KILL
|
||||
retry 10 1 eval "ROOT='$2' __runc state '$1' | grep -q 'stopped'"
|
||||
ROOT=$2 runc delete $1
|
||||
fi
|
||||
}
|
||||
|
||||
function teardown_busybox() {
|
||||
cd "$INTEGRATION_ROOT"
|
||||
teardown_recvtty
|
||||
teardown_running_container test_busybox
|
||||
run rm -f -r "$BUSYBOX_BUNDLE"
|
||||
}
|
||||
|
||||
function teardown_hello() {
|
||||
cd "$INTEGRATION_ROOT"
|
||||
teardown_recvtty
|
||||
teardown_running_container test_hello
|
||||
run rm -f -r "$HELLO_BUNDLE"
|
||||
}
|
30
vendor/github.com/opencontainers/runc/tests/integration/kill.bats
generated
vendored
Normal file
30
vendor/github.com/opencontainers/runc/tests/integration/kill.bats
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
|
||||
@test "kill detached busybox" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
testcontainer test_busybox running
|
||||
|
||||
runc kill test_busybox KILL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'stopped'"
|
||||
|
||||
runc delete test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
56
vendor/github.com/opencontainers/runc/tests/integration/list.bats
generated
vendored
Normal file
56
vendor/github.com/opencontainers/runc/tests/integration/list.bats
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_running_container_inroot test_box1 $HELLO_BUNDLE
|
||||
teardown_running_container_inroot test_box2 $HELLO_BUNDLE
|
||||
teardown_running_container_inroot test_box3 $HELLO_BUNDLE
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_running_container_inroot test_box1 $HELLO_BUNDLE
|
||||
teardown_running_container_inroot test_box2 $HELLO_BUNDLE
|
||||
teardown_running_container_inroot test_box3 $HELLO_BUNDLE
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "list" {
|
||||
# run a few busyboxes detached
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console-socket $CONSOLE_SOCKET test_box1
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console-socket $CONSOLE_SOCKET test_box2
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console-socket $CONSOLE_SOCKET test_box3
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc list
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ ID\ +PID\ +STATUS\ +BUNDLE\ +CREATED+ ]]
|
||||
[[ "${lines[1]}" == *"test_box1"*[0-9]*"running"*$BUSYBOX_BUNDLE*[0-9]* ]]
|
||||
[[ "${lines[2]}" == *"test_box2"*[0-9]*"running"*$BUSYBOX_BUNDLE*[0-9]* ]]
|
||||
[[ "${lines[3]}" == *"test_box3"*[0-9]*"running"*$BUSYBOX_BUNDLE*[0-9]* ]]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc list -q
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[0]}" == "test_box1" ]]
|
||||
[[ "${lines[1]}" == "test_box2" ]]
|
||||
[[ "${lines[2]}" == "test_box3" ]]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc list --format table
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[0]} =~ ID\ +PID\ +STATUS\ +BUNDLE\ +CREATED+ ]]
|
||||
[[ "${lines[1]}" == *"test_box1"*[0-9]*"running"*$BUSYBOX_BUNDLE*[0-9]* ]]
|
||||
[[ "${lines[2]}" == *"test_box2"*[0-9]*"running"*$BUSYBOX_BUNDLE*[0-9]* ]]
|
||||
[[ "${lines[3]}" == *"test_box3"*[0-9]*"running"*$BUSYBOX_BUNDLE*[0-9]* ]]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc list --format json
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[0]}" == [\[][\{]"\"ociVersion\""[:]"\""*[0-9][\.]*[0-9][\.]*[0-9]*"\""[,]"\"id\""[:]"\"test_box1\""[,]"\"pid\""[:]*[0-9][,]"\"status\""[:]*"\"running\""[,]"\"bundle\""[:]*$BUSYBOX_BUNDLE*[,]"\"rootfs\""[:]"\""*"\""[,]"\"created\""[:]*[0-9]*[\}]* ]]
|
||||
[[ "${lines[0]}" == *[,][\{]"\"ociVersion\""[:]"\""*[0-9][\.]*[0-9][\.]*[0-9]*"\""[,]"\"id\""[:]"\"test_box2\""[,]"\"pid\""[:]*[0-9][,]"\"status\""[:]*"\"running\""[,]"\"bundle\""[:]*$BUSYBOX_BUNDLE*[,]"\"rootfs\""[:]"\""*"\""[,]"\"created\""[:]*[0-9]*[\}]* ]]
|
||||
[[ "${lines[0]}" == *[,][\{]"\"ociVersion\""[:]"\""*[0-9][\.]*[0-9][\.]*[0-9]*"\""[,]"\"id\""[:]"\"test_box3\""[,]"\"pid\""[:]*[0-9][,]"\"status\""[:]*"\"running\""[,]"\"bundle\""[:]*$BUSYBOX_BUNDLE*[,]"\"rootfs\""[:]"\""*"\""[,]"\"created\""[:]*[0-9]*[\}][\]] ]]
|
||||
}
|
59
vendor/github.com/opencontainers/runc/tests/integration/mask.bats
generated
vendored
Normal file
59
vendor/github.com/opencontainers/runc/tests/integration/mask.bats
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
|
||||
# Create fake rootfs.
|
||||
mkdir rootfs/testdir
|
||||
echo "Forbidden information!" > rootfs/testfile
|
||||
|
||||
# add extra masked paths
|
||||
sed -i 's;"maskedPaths": \[;"maskedPaths": \["/testdir","/testfile",;g' config.json
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "mask paths [file]" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec test_busybox cat /testfile
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == "" ]]
|
||||
|
||||
runc exec test_busybox rm -f /testfile
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"Read-only file system"* ]]
|
||||
|
||||
runc exec test_busybox umount /testfile
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"Operation not permitted"* ]]
|
||||
}
|
||||
|
||||
@test "mask paths [directory]" {
|
||||
# run busybox detached
|
||||
runc run -d --console-socket $CONSOLE_SOCKET test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
runc exec test_busybox ls /testdir
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == "" ]]
|
||||
|
||||
runc exec test_busybox touch /testdir/foo
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"Read-only file system"* ]]
|
||||
|
||||
runc exec test_busybox rm -rf /testdir
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"Read-only file system"* ]]
|
||||
|
||||
runc exec test_busybox umount /testdir
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "${output}" == *"Operation not permitted"* ]]
|
||||
}
|
21
vendor/github.com/opencontainers/runc/tests/integration/mounts.bats
generated
vendored
Executable file
21
vendor/github.com/opencontainers/runc/tests/integration/mounts.bats
generated
vendored
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "runc run [bind mount]" {
|
||||
CONFIG=$(jq '.mounts |= . + [{"source": ".", "destination": "/tmp/bind", "options": ["bind"]}] | .process.args = ["ls", "/tmp/bind/config.json"]' config.json)
|
||||
echo "${CONFIG}" >config.json
|
||||
|
||||
runc run test_bind_mount
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${lines[0]}" =~ '/tmp/bind/config.json' ]]
|
||||
}
|
22
vendor/github.com/opencontainers/runc/tests/integration/multi-arch.bash
generated
vendored
Normal file
22
vendor/github.com/opencontainers/runc/tests/integration/multi-arch.bash
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
get_busybox(){
|
||||
case $(go env GOARCH) in
|
||||
arm64)
|
||||
echo 'https://github.com/docker-library/busybox/raw/23fbd9c43e0f4bec7605091bfba23db278c367ac/glibc/busybox.tar.xz'
|
||||
;;
|
||||
*)
|
||||
echo 'https://github.com/docker-library/busybox/raw/a0558a9006ce0dd6f6ec5d56cfd3f32ebeeb815f/glibc/busybox.tar.xz'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_hello(){
|
||||
case $(go env GOARCH) in
|
||||
arm64)
|
||||
echo 'hello-world-aarch64.tar'
|
||||
;;
|
||||
*)
|
||||
echo 'hello-world.tar'
|
||||
;;
|
||||
esac
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue