Merge pull request #1559 from Mashimiao/panic-fix-nil-linux
fix panic when Linux is nil for rootless case
diff --git a/.travis.yml b/.travis.yml
index a9032a2..fd8f79f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,4 +30,4 @@
script:
- git-validation -run DCO,short-subject -v
- make BUILDTAGS="${BUILDTAGS}"
- - make BUILDTAGS="${BUILDTAGS}" clean validate test
+ - make BUILDTAGS="${BUILDTAGS}" clean ci
diff --git a/Makefile b/Makefile
index dc13d2e..0877a07 100644
--- a/Makefile
+++ b/Makefile
@@ -29,43 +29,21 @@
.DEFAULT: runc
runc: $(SOURCES)
- $(GO) build $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc .
+ $(GO) build -buildmode=pie $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o runc .
all: runc recvtty
recvtty: contrib/cmd/recvtty/recvtty
contrib/cmd/recvtty/recvtty: $(SOURCES)
- $(GO) build $(EXTRA_FLAGS) -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -tags "$(BUILDTAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
+ $(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) cgo static_build" -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) cgo static_build" -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION} $(EXTRA_LDFLAGS)" -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty
release:
- @flag_list=(seccomp selinux apparmor static); \
- unset expression; \
- for flag in "$${flag_list[@]}"; do \
- expression+="' '{'',$${flag}}"; \
- done; \
- eval profile_list=("$$expression"); \
- for profile in "$${profile_list[@]}"; do \
- output=${RELEASE_DIR}/runc; \
- for flag in $$profile; do \
- output+=."$$flag"; \
- done; \
- tags="$$profile"; \
- ldflags="-X main.gitCommit=${COMMIT} -X main.version=${VERSION}"; \
- CGO_ENABLED=; \
- [[ "$$profile" =~ static ]] && { \
- tags="$${tags/static/static_build}"; \
- tags+=" cgo"; \
- ldflags+=" -w -extldflags -static"; \
- CGO_ENABLED=1; \
- }; \
- echo "Building target: $$output"; \
- $(GO) build $(EXTRA_FLAGS) -ldflags "$$ldflags $(EXTRA_LDFLAGS)" -tags "$$tags" -o "$$output" .; \
- done
+ script/release.sh -r release/$(VERSION) -v $(VERSION)
dbuild: runcimage
docker run --rm -v $(CURDIR):/go/src/$(PROJECT) --privileged $(RUNC_IMAGE) make clean all
@@ -137,7 +115,7 @@
script/validate-gofmt
$(GO) vet $(allpackages)
-ci: validate localtest
+ci: validate test release
# memoize allpackages, so that it's executed only once and only if used
_allpackages = $(shell $(GO) list ./... | grep -v vendor)
diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go
index d7e7516..cbdfe0d 100644
--- a/libcontainer/container_linux.go
+++ b/libcontainer/container_linux.go
@@ -186,8 +186,17 @@
if status == Stopped {
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
}
+ if err := c.cgroupManager.Set(&config); err != nil {
+ // Set configs back
+ if err2 := c.cgroupManager.Set(c.config); err2 != nil {
+ logrus.Warnf("Setting back cgroup configs failed due to error: %v, your state.json and actual configs might be inconsistent.", err2)
+ }
+ return err
+ }
+ // After config setting succeed, update config and states
c.config = &config
- return c.cgroupManager.Set(c.config)
+ _, err = c.updateState(nil)
+ return err
}
func (c *linuxContainer) Start(process *Process) error {
@@ -1388,7 +1397,9 @@
}
func (c *linuxContainer) updateState(process parentProcess) (*State, error) {
- c.initProcess = process
+ if process != nil {
+ c.initProcess = process
+ }
state, err := c.currentState()
if err != nil {
return nil, err
diff --git a/libcontainer/container_linux_test.go b/libcontainer/container_linux_test.go
index 3fdd4a8..75c2898 100644
--- a/libcontainer/container_linux_test.go
+++ b/libcontainer/container_linux_test.go
@@ -9,6 +9,7 @@
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
+ "github.com/opencontainers/runc/libcontainer/system"
)
type mockCgroupManager struct {
@@ -216,3 +217,65 @@
}
}
}
+
+func TestGetContainerStateAfterUpdate(t *testing.T) {
+ var (
+ pid = os.Getpid()
+ )
+ stat, err := system.Stat(pid)
+ if err != nil {
+ t.Fatal(err)
+ }
+ container := &linuxContainer{
+ id: "myid",
+ config: &configs.Config{
+ Namespaces: []configs.Namespace{
+ {Type: configs.NEWPID},
+ {Type: configs.NEWNS},
+ {Type: configs.NEWNET},
+ {Type: configs.NEWUTS},
+ {Type: configs.NEWIPC},
+ },
+ Cgroups: &configs.Cgroup{
+ Resources: &configs.Resources{
+ Memory: 1024,
+ },
+ },
+ },
+ initProcess: &mockProcess{
+ _pid: pid,
+ started: stat.StartTime,
+ },
+ cgroupManager: &mockCgroupManager{},
+ }
+ container.state = &createdState{c: container}
+ state, err := container.State()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if state.InitProcessPid != pid {
+ t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
+ }
+ if state.InitProcessStartTime != stat.StartTime {
+ t.Fatalf("expected process start time %d but received %d", stat.StartTime, state.InitProcessStartTime)
+ }
+ if state.Config.Cgroups.Resources.Memory != 1024 {
+ t.Fatalf("expected Memory to be 1024 but received %q", state.Config.Cgroups.Memory)
+ }
+
+ // Set initProcessStartTime so we fake to be running
+ container.initProcessStartTime = state.InitProcessStartTime
+ container.state = &runningState{c: container}
+ newConfig := container.Config()
+ newConfig.Cgroups.Resources.Memory = 2048
+ if err := container.Set(newConfig); err != nil {
+ t.Fatal(err)
+ }
+ state, err = container.State()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if state.Config.Cgroups.Resources.Memory != 2048 {
+ t.Fatalf("expected Memory to be 2048 but received %q", state.Config.Cgroups.Memory)
+ }
+}
diff --git a/script/release.sh b/script/release.sh
new file mode 100755
index 0000000..a1ebc95
--- /dev/null
+++ b/script/release.sh
@@ -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,}